meno-core 1.0.39 → 1.0.41

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 (67) hide show
  1. package/bin/cli.ts +33 -0
  2. package/build-astro.ts +172 -69
  3. package/dist/bin/cli.js +30 -2
  4. package/dist/bin/cli.js.map +2 -2
  5. package/dist/build-static.js +7 -7
  6. package/dist/chunks/{chunk-WK5XLASY.js → chunk-EQOSDQS2.js} +4 -4
  7. package/dist/chunks/{chunk-AIXKUVNG.js → chunk-IBR2F4IL.js} +4 -5
  8. package/dist/chunks/{chunk-AIXKUVNG.js.map → chunk-IBR2F4IL.js.map} +2 -2
  9. package/dist/chunks/{chunk-NV25WXCA.js → chunk-IGVQF5GY.js} +11 -7
  10. package/dist/chunks/chunk-IGVQF5GY.js.map +7 -0
  11. package/dist/chunks/{chunk-KULPBDC7.js → chunk-LBWIHPN7.js} +9 -3
  12. package/dist/chunks/chunk-LBWIHPN7.js.map +7 -0
  13. package/dist/chunks/{chunk-A6KWUEA6.js → chunk-MKB2J6AD.js} +9 -1
  14. package/dist/chunks/chunk-MKB2J6AD.js.map +7 -0
  15. package/dist/chunks/{chunk-P3FX5HJM.js → chunk-S2HXJTAF.js} +1 -1
  16. package/dist/chunks/chunk-S2HXJTAF.js.map +7 -0
  17. package/dist/chunks/{chunk-W6HDII4T.js → chunk-SK3TLNUP.js} +140 -114
  18. package/dist/chunks/chunk-SK3TLNUP.js.map +7 -0
  19. package/dist/chunks/{chunk-HNAS6BSS.js → chunk-SNUROC7E.js} +56 -6
  20. package/dist/chunks/{chunk-HNAS6BSS.js.map → chunk-SNUROC7E.js.map} +3 -3
  21. package/dist/chunks/{configService-TXBNUBBL.js → configService-MICL4S2L.js} +2 -2
  22. package/dist/chunks/{constants-5CRJRQNR.js → constants-ZEU4TZCA.js} +2 -2
  23. package/dist/entries/server-router.js +7 -7
  24. package/dist/lib/client/index.js +11 -6
  25. package/dist/lib/client/index.js.map +2 -2
  26. package/dist/lib/server/index.js +507 -1587
  27. package/dist/lib/server/index.js.map +4 -4
  28. package/dist/lib/shared/index.js +3 -3
  29. package/dist/lib/test-utils/index.js +1 -1
  30. package/lib/client/core/ComponentBuilder.ts +1 -1
  31. package/lib/client/core/builders/embedBuilder.ts +2 -2
  32. package/lib/client/routing/Router.tsx +6 -0
  33. package/lib/client/templateEngine.test.ts +178 -0
  34. package/lib/client/templateEngine.ts +1 -2
  35. package/lib/server/astro/cmsPageEmitter.ts +420 -0
  36. package/lib/server/astro/componentEmitter.ts +150 -17
  37. package/lib/server/astro/nodeToAstro.test.ts +1101 -0
  38. package/lib/server/astro/nodeToAstro.ts +869 -37
  39. package/lib/server/astro/pageEmitter.ts +43 -3
  40. package/lib/server/astro/tailwindMapper.ts +69 -8
  41. package/lib/server/astro/templateTransformer.ts +107 -0
  42. package/lib/server/index.ts +26 -3
  43. package/lib/server/routes/api/components.ts +62 -0
  44. package/lib/server/routes/api/core-routes.ts +8 -0
  45. package/lib/server/services/configService.ts +12 -0
  46. package/lib/server/ssr/htmlGenerator.ts +0 -5
  47. package/lib/server/ssr/imageMetadata.ts +3 -3
  48. package/lib/server/ssr/ssrRenderer.ts +78 -29
  49. package/lib/server/webflow/buildWebflow.ts +415 -0
  50. package/lib/server/webflow/index.ts +22 -0
  51. package/lib/server/webflow/nodeToWebflow.ts +423 -0
  52. package/lib/server/webflow/styleMapper.ts +241 -0
  53. package/lib/server/webflow/types.ts +196 -0
  54. package/lib/shared/constants.ts +4 -0
  55. package/lib/shared/types/components.ts +9 -4
  56. package/lib/shared/validation/propValidator.ts +2 -1
  57. package/lib/shared/validation/schemas.ts +4 -1
  58. package/package.json +1 -1
  59. package/templates/index-router.html +0 -5
  60. package/dist/chunks/chunk-A6KWUEA6.js.map +0 -7
  61. package/dist/chunks/chunk-KULPBDC7.js.map +0 -7
  62. package/dist/chunks/chunk-NV25WXCA.js.map +0 -7
  63. package/dist/chunks/chunk-P3FX5HJM.js.map +0 -7
  64. package/dist/chunks/chunk-W6HDII4T.js.map +0 -7
  65. /package/dist/chunks/{chunk-WK5XLASY.js.map → chunk-EQOSDQS2.js.map} +0 -0
  66. /package/dist/chunks/{configService-TXBNUBBL.js.map → configService-MICL4S2L.js.map} +0 -0
  67. /package/dist/chunks/{constants-5CRJRQNR.js.map → constants-ZEU4TZCA.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-WK5XLASY.js";
6
+ } from "../../chunks/chunk-EQOSDQS2.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-HNAS6BSS.js";
35
+ } from "../../chunks/chunk-SNUROC7E.js";
36
36
  import {
37
37
  CMSService,
38
38
  ColorService,
@@ -46,15 +46,12 @@ import {
46
46
  clearJSValidationCache,
47
47
  collectComponentCSS,
48
48
  collectComponentJavaScript,
49
- collectComponentLibraries,
50
49
  colorService,
51
50
  createI18nResolver,
52
51
  escapeHtml,
53
52
  extractPageMeta,
54
- filterLibrariesByContext,
55
53
  generateFontCSS,
56
54
  generateFontPreloadTags,
57
- generateLibraryTags,
58
55
  generateMetaTags,
59
56
  generateSSRHTML,
60
57
  generateThemeColorVariablesCSS,
@@ -67,7 +64,6 @@ import {
67
64
  loadProjectConfig,
68
65
  loadResponsiveScalesConfig,
69
66
  mapPageNameToPath,
70
- mergeLibraries,
71
67
  migrateTemplatesDirectory,
72
68
  parseJSON,
73
69
  processCMSPropsTemplate,
@@ -76,11 +72,11 @@ import {
76
72
  resetFontConfig,
77
73
  styleToString,
78
74
  variableService
79
- } from "../../chunks/chunk-W6HDII4T.js";
75
+ } from "../../chunks/chunk-SK3TLNUP.js";
80
76
  import {
81
77
  ConfigService,
82
78
  configService
83
- } from "../../chunks/chunk-A6KWUEA6.js";
79
+ } from "../../chunks/chunk-MKB2J6AD.js";
84
80
  import {
85
81
  bundleFile,
86
82
  createRuntimeServer,
@@ -110,18 +106,14 @@ import {
110
106
  writeFile
111
107
  } from "../../chunks/chunk-WQFG7PAH.js";
112
108
  import "../../chunks/chunk-LIHJ6OUH.js";
113
- import "../../chunks/chunk-P3FX5HJM.js";
114
- import "../../chunks/chunk-AIXKUVNG.js";
109
+ import "../../chunks/chunk-S2HXJTAF.js";
110
+ import "../../chunks/chunk-IBR2F4IL.js";
115
111
  import {
116
- extractInteractiveStyleMappings,
117
- generateAllInteractiveCSS,
118
112
  generateElementClassName,
119
- hasInteractiveStyleMappings,
120
113
  isItemDraftForLocale,
121
114
  isVoidElement
122
- } from "../../chunks/chunk-NV25WXCA.js";
115
+ } from "../../chunks/chunk-IGVQF5GY.js";
123
116
  import {
124
- DEFAULT_BREAKPOINTS,
125
117
  isI18nValue,
126
118
  resolveI18nValue
127
119
  } from "../../chunks/chunk-PGH3ATYI.js";
@@ -133,603 +125,189 @@ import {
133
125
  SERVER_PORT,
134
126
  SERVE_PORT,
135
127
  init_constants
136
- } from "../../chunks/chunk-KULPBDC7.js";
128
+ } from "../../chunks/chunk-LBWIHPN7.js";
137
129
  import "../../chunks/chunk-KSBZ2L7C.js";
138
130
 
139
- // build-astro.ts
140
- import { existsSync, readdirSync, mkdirSync, rmSync, statSync, copyFileSync } from "fs";
141
- import { writeFile as writeFile2, readFile } from "fs/promises";
131
+ // lib/server/webflow/buildWebflow.ts
132
+ import { existsSync, readdirSync } from "fs";
142
133
  import { join } from "path";
143
- import { createHash } from "crypto";
144
134
 
145
- // lib/server/astro/tailwindMapper.ts
135
+ // lib/server/webflow/types.ts
136
+ function mapCMSFieldType(menoType) {
137
+ switch (menoType) {
138
+ case "string":
139
+ return "PlainText";
140
+ case "text":
141
+ case "rich-text":
142
+ return "RichText";
143
+ case "number":
144
+ return "Number";
145
+ case "boolean":
146
+ return "Switch";
147
+ case "image":
148
+ return "Image";
149
+ case "date":
150
+ return "Date";
151
+ case "select":
152
+ return "Option";
153
+ case "file":
154
+ return "File";
155
+ case "reference":
156
+ return "Reference";
157
+ default:
158
+ return "PlainText";
159
+ }
160
+ }
161
+
162
+ // lib/server/webflow/nodeToWebflow.ts
163
+ init_constants();
164
+
165
+ // lib/server/webflow/styleMapper.ts
166
+ var UNITLESS_PROPERTIES = /* @__PURE__ */ new Set([
167
+ "opacity",
168
+ "z-index",
169
+ "flex-grow",
170
+ "flex-shrink",
171
+ "flex",
172
+ "order",
173
+ "orphans",
174
+ "widows",
175
+ "column-count",
176
+ "font-weight",
177
+ "tab-size"
178
+ ]);
146
179
  function isStyleMapping(value) {
147
180
  return typeof value === "object" && value !== null && "_mapping" in value && value._mapping === true;
148
181
  }
149
- function hasTemplateExpression(value) {
150
- return /\{\{.+?\}\}/.test(value);
151
- }
152
182
  function isResponsiveStyle(style) {
153
183
  return "base" in style || "tablet" in style || "mobile" in style;
154
184
  }
155
- var exactMatches = {
156
- display: {
157
- flex: "flex",
158
- grid: "grid",
159
- block: "block",
160
- none: "hidden",
161
- inline: "inline",
162
- "inline-block": "inline-block",
163
- "inline-flex": "inline-flex",
164
- "inline-grid": "inline-grid"
165
- },
166
- flexDirection: {
167
- column: "flex-col",
168
- row: "flex-row",
169
- "column-reverse": "flex-col-reverse",
170
- "row-reverse": "flex-row-reverse"
171
- },
172
- justifyContent: {
173
- center: "justify-center",
174
- "flex-start": "justify-start",
175
- "flex-end": "justify-end",
176
- "space-between": "justify-between",
177
- "space-around": "justify-around",
178
- "space-evenly": "justify-evenly",
179
- // camelCase aliases (component data uses camelCase)
180
- spaceBetween: "justify-between",
181
- spaceAround: "justify-around",
182
- spaceEvenly: "justify-evenly",
183
- flexStart: "justify-start",
184
- flexEnd: "justify-end"
185
- },
186
- alignItems: {
187
- center: "items-center",
188
- "flex-start": "items-start",
189
- "flex-end": "items-end",
190
- stretch: "items-stretch",
191
- baseline: "items-baseline",
192
- // camelCase aliases
193
- flexStart: "items-start",
194
- flexEnd: "items-end"
195
- },
196
- alignContent: {
197
- center: "content-center",
198
- "flex-start": "content-start",
199
- "flex-end": "content-end",
200
- "space-between": "content-between",
201
- "space-around": "content-around",
202
- stretch: "content-stretch"
203
- },
204
- alignSelf: {
205
- auto: "self-auto",
206
- center: "self-center",
207
- "flex-start": "self-start",
208
- "flex-end": "self-end",
209
- stretch: "self-stretch"
210
- },
211
- position: {
212
- relative: "relative",
213
- absolute: "absolute",
214
- fixed: "fixed",
215
- sticky: "sticky",
216
- static: "static"
217
- },
218
- overflow: {
219
- hidden: "overflow-hidden",
220
- auto: "overflow-auto",
221
- scroll: "overflow-scroll",
222
- visible: "overflow-visible"
223
- },
224
- overflowX: {
225
- hidden: "overflow-x-hidden",
226
- auto: "overflow-x-auto",
227
- scroll: "overflow-x-scroll",
228
- visible: "overflow-x-visible"
229
- },
230
- overflowY: {
231
- hidden: "overflow-y-hidden",
232
- auto: "overflow-y-auto",
233
- scroll: "overflow-y-scroll",
234
- visible: "overflow-y-visible"
235
- },
236
- cursor: {
237
- pointer: "cursor-pointer",
238
- default: "cursor-default",
239
- "not-allowed": "cursor-not-allowed",
240
- grab: "cursor-grab",
241
- grabbing: "cursor-grabbing",
242
- text: "cursor-text",
243
- move: "cursor-move",
244
- wait: "cursor-wait"
245
- },
246
- textAlign: {
247
- center: "text-center",
248
- left: "text-left",
249
- right: "text-right",
250
- justify: "text-justify"
251
- },
252
- textDecoration: {
253
- none: "no-underline",
254
- underline: "underline",
255
- "line-through": "line-through",
256
- overline: "overline"
257
- },
258
- textTransform: {
259
- uppercase: "uppercase",
260
- lowercase: "lowercase",
261
- capitalize: "capitalize",
262
- none: "normal-case"
263
- },
264
- objectFit: {
265
- cover: "object-cover",
266
- contain: "object-contain",
267
- fill: "object-fill",
268
- none: "object-none",
269
- "scale-down": "object-scale-down"
270
- },
271
- objectPosition: {
272
- center: "object-center",
273
- top: "object-top",
274
- bottom: "object-bottom",
275
- left: "object-left",
276
- right: "object-right"
277
- },
278
- flexWrap: {
279
- wrap: "flex-wrap",
280
- nowrap: "flex-nowrap",
281
- "wrap-reverse": "flex-wrap-reverse"
282
- },
283
- pointerEvents: {
284
- none: "pointer-events-none",
285
- auto: "pointer-events-auto"
286
- },
287
- userSelect: {
288
- none: "select-none",
289
- auto: "select-auto",
290
- text: "select-text",
291
- all: "select-all"
292
- },
293
- visibility: {
294
- hidden: "invisible",
295
- visible: "visible"
296
- },
297
- whiteSpace: {
298
- normal: "whitespace-normal",
299
- nowrap: "whitespace-nowrap",
300
- pre: "whitespace-pre",
301
- "pre-wrap": "whitespace-pre-wrap",
302
- "pre-line": "whitespace-pre-line"
303
- },
304
- wordBreak: {
305
- "break-all": "break-all",
306
- "break-word": "break-words",
307
- normal: "break-normal"
308
- },
309
- listStyleType: {
310
- none: "list-none",
311
- disc: "list-disc",
312
- decimal: "list-decimal"
313
- },
314
- listStylePosition: {
315
- inside: "list-inside",
316
- outside: "list-outside"
317
- },
318
- boxSizing: {
319
- "border-box": "box-border",
320
- "content-box": "box-content"
321
- },
322
- gridAutoFlow: {
323
- row: "grid-flow-row",
324
- column: "grid-flow-col",
325
- dense: "grid-flow-dense",
326
- "row dense": "grid-flow-row-dense",
327
- "column dense": "grid-flow-col-dense"
328
- }
329
- };
330
- var singleValueMatches = {
331
- // width/height 100%
332
- "width:100%": "w-full",
333
- "height:100%": "h-full",
334
- "width:100vw": "w-screen",
335
- "height:100vh": "h-screen",
336
- "width:auto": "w-auto",
337
- "height:auto": "h-auto",
338
- "width:fit-content": "w-fit",
339
- "height:fit-content": "h-fit",
340
- "width:min-content": "w-min",
341
- "height:min-content": "h-min",
342
- "width:max-content": "w-max",
343
- "height:max-content": "h-max",
344
- "maxWidth:100%": "max-w-full",
345
- "maxWidth:none": "max-w-none",
346
- "maxHeight:100%": "max-h-full",
347
- "maxHeight:none": "max-h-none",
348
- "minWidth:0": "min-w-0",
349
- "minHeight:0": "min-h-0",
350
- "margin:auto": "m-auto",
351
- "margin:0 auto": "mx-auto",
352
- "marginLeft:auto": "ml-auto",
353
- "marginRight:auto": "mr-auto",
354
- "marginInline:auto": "mx-auto",
355
- "borderRadius:50%": "rounded-full",
356
- "borderRadius:9999px": "rounded-full",
357
- "borderRadius:0": "rounded-none",
358
- "flex:1": "flex-1",
359
- "flex:none": "flex-none",
360
- "flex:auto": "flex-auto",
361
- "flexGrow:0": "grow-0",
362
- "flexGrow:1": "grow",
363
- "flexShrink:0": "shrink-0",
364
- "flexShrink:1": "shrink",
365
- "opacity:0": "opacity-0",
366
- "opacity:1": "opacity-100",
367
- "zIndex:0": "z-0",
368
- "zIndex:10": "z-10",
369
- "zIndex:20": "z-20",
370
- "zIndex:30": "z-30",
371
- "zIndex:40": "z-40",
372
- "zIndex:50": "z-50",
373
- "inset:0": "inset-0",
374
- "top:0": "top-0",
375
- "right:0": "right-0",
376
- "bottom:0": "bottom-0",
377
- "left:0": "left-0"
378
- };
379
- var arbitraryPrefixMap = {
380
- // Spacing
381
- padding: "p",
382
- paddingTop: "pt",
383
- paddingRight: "pr",
384
- paddingBottom: "pb",
385
- paddingLeft: "pl",
386
- paddingInline: "px",
387
- paddingBlock: "py",
388
- margin: "m",
389
- marginTop: "mt",
390
- marginRight: "mr",
391
- marginBottom: "mb",
392
- marginLeft: "ml",
393
- marginInline: "mx",
394
- marginBlock: "my",
395
- gap: "gap",
396
- rowGap: "gap-y",
397
- columnGap: "gap-x",
398
- // Sizing
399
- width: "w",
400
- height: "h",
401
- maxWidth: "max-w",
402
- maxHeight: "max-h",
403
- minWidth: "min-w",
404
- minHeight: "min-h",
405
- // Typography
406
- fontSize: "text",
407
- fontWeight: "font",
408
- fontFamily: "font",
409
- lineHeight: "leading",
410
- letterSpacing: "tracking",
411
- // Borders
412
- borderRadius: "rounded",
413
- borderTopLeftRadius: "rounded-tl",
414
- borderTopRightRadius: "rounded-tr",
415
- borderBottomLeftRadius: "rounded-bl",
416
- borderBottomRightRadius: "rounded-br",
417
- border: "border",
418
- borderTop: "border-t",
419
- borderRight: "border-r",
420
- borderBottom: "border-b",
421
- borderLeft: "border-l",
422
- borderColor: "border",
423
- // Colors
424
- color: "text",
425
- backgroundColor: "bg",
426
- background: "bg",
427
- backgroundImage: "bg",
428
- // Effects
429
- opacity: "opacity",
430
- boxShadow: "shadow",
431
- textShadow: "[text-shadow]",
432
- filter: "[filter]",
433
- backdropFilter: "backdrop",
434
- transform: "[transform]",
435
- transformOrigin: "origin",
436
- transition: "transition",
437
- mixBlendMode: "mix-blend",
438
- clipPath: "[clip-path]",
439
- // Positioning
440
- top: "top",
441
- right: "right",
442
- bottom: "bottom",
443
- left: "left",
444
- inset: "inset",
445
- zIndex: "z",
446
- // Grid
447
- gridTemplateColumns: "grid-cols",
448
- gridTemplateRows: "grid-rows",
449
- gridColumn: "col",
450
- gridRow: "row",
451
- gridAutoRows: "auto-rows",
452
- gridAutoColumns: "auto-cols",
453
- // Flexbox extras
454
- flexGrow: "grow",
455
- flexShrink: "shrink",
456
- flexBasis: "basis",
457
- order: "order",
458
- flex: "flex",
459
- // Aspect ratio
460
- aspectRatio: "aspect",
461
- // Outline
462
- outline: "outline",
463
- outlineWidth: "outline",
464
- outlineOffset: "outline-offset",
465
- outlineColor: "outline",
466
- // Other
467
- accentColor: "accent",
468
- textIndent: "[text-indent]",
469
- verticalAlign: "align",
470
- overflowWrap: "[overflow-wrap]",
471
- scrollBehavior: "scroll",
472
- resize: "resize"
473
- };
474
- function propertyToTailwind(property, value) {
475
- const strValue = String(value);
476
- if (strValue === "") return null;
477
- if (hasTemplateExpression(strValue)) return null;
478
- const singleKey = `${property}:${strValue}`;
479
- if (singleValueMatches[singleKey]) {
480
- return singleValueMatches[singleKey];
481
- }
482
- if (exactMatches[property]?.[strValue]) {
483
- return exactMatches[property][strValue];
484
- }
485
- if (property === "color" || property === "backgroundColor" || property === "borderColor") {
486
- const prefix = property === "color" ? "text" : property === "backgroundColor" ? "bg" : "border";
487
- if (strValue.includes("var(")) {
488
- return `${prefix}-[${strValue}]`;
489
- }
490
- if (!strValue.match(/^[#\d]/) && !strValue.includes("rgb") && !strValue.includes("hsl")) {
491
- return `${prefix}-[var(--${strValue})]`;
492
- }
493
- }
494
- const twPrefix = arbitraryPrefixMap[property];
495
- if (!twPrefix) {
496
- const cssProp = property.replace(/([A-Z])/g, "-$1").toLowerCase();
497
- const sanitized2 = strValue.replace(/\s+/g, "_");
498
- return `[${cssProp}:${sanitized2}]`;
499
- }
500
- if (twPrefix.startsWith("[")) {
501
- const sanitized2 = strValue.replace(/\s+/g, "_");
502
- return `${twPrefix.slice(0, -1)}:${sanitized2}]`;
503
- }
504
- const sanitized = strValue.replace(/\s+/g, "_");
505
- if (property === "fontFamily" && !sanitized.includes(",") && !sanitized.startsWith("'")) {
506
- return `${twPrefix}-['${sanitized}']`;
507
- }
508
- return `${twPrefix}-[${sanitized}]`;
185
+ function toKebabCase(prop) {
186
+ return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
509
187
  }
510
- function stylesToTailwind(style) {
511
- const classes = [];
512
- const dynamicStyles = {};
188
+ function styleObjectToCSS(style) {
189
+ const css = {};
513
190
  for (const [prop, value] of Object.entries(style)) {
514
191
  if (isStyleMapping(value)) continue;
515
- const strValue = String(value);
516
- if (hasTemplateExpression(strValue)) {
517
- const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
518
- dynamicStyles[cssProp] = strValue;
519
- continue;
520
- }
521
- const twClass = propertyToTailwind(prop, value);
522
- if (twClass) {
523
- classes.push(twClass);
524
- }
525
- }
526
- return { classes, dynamicStyles };
527
- }
528
- function responsiveStylesToTailwind(style, breakpoints) {
529
- if (!style) return { classes: [], dynamicStyles: {} };
530
- const allClasses = [];
531
- const allDynamicStyles = {};
532
- if (isResponsiveStyle(style)) {
533
- const responsive = style;
534
- if (responsive.base) {
535
- const { classes, dynamicStyles } = stylesToTailwind(responsive.base);
536
- allClasses.push(...classes);
537
- Object.assign(allDynamicStyles, dynamicStyles);
538
- }
539
- if (responsive.tablet) {
540
- const bpValue = breakpoints.tablet?.breakpoint ?? 1024;
541
- const prefix = `max-[${bpValue}px]:`;
542
- const { classes, dynamicStyles } = stylesToTailwind(responsive.tablet);
543
- allClasses.push(...classes.map((cls) => `${prefix}${cls}`));
544
- Object.assign(allDynamicStyles, dynamicStyles);
545
- }
546
- if (responsive.mobile) {
547
- const bpValue = breakpoints.mobile?.breakpoint ?? 540;
548
- const prefix = `max-[${bpValue}px]:`;
549
- const { classes, dynamicStyles } = stylesToTailwind(responsive.mobile);
550
- allClasses.push(...classes.map((cls) => `${prefix}${cls}`));
551
- Object.assign(allDynamicStyles, dynamicStyles);
552
- }
553
- for (const [bpName, bpStyle] of Object.entries(responsive)) {
554
- if (bpName === "base" || bpName === "tablet" || bpName === "mobile" || !bpStyle) continue;
555
- const bpValue = breakpoints[bpName]?.breakpoint;
556
- if (!bpValue) continue;
557
- const prefix = `max-[${bpValue}px]:`;
558
- const { classes, dynamicStyles } = stylesToTailwind(bpStyle);
559
- allClasses.push(...classes.map((cls) => `${prefix}${cls}`));
560
- Object.assign(allDynamicStyles, dynamicStyles);
192
+ if (value === "" || value === void 0 || value === null) continue;
193
+ if (typeof value === "boolean" || typeof value === "object") continue;
194
+ const cssProp = toKebabCase(prop);
195
+ if (typeof value === "number") {
196
+ if (isNaN(value)) continue;
197
+ css[cssProp] = UNITLESS_PROPERTIES.has(cssProp) ? String(value) : `${value}px`;
198
+ } else {
199
+ css[cssProp] = String(value);
561
200
  }
562
- } else {
563
- const { classes, dynamicStyles } = stylesToTailwind(style);
564
- allClasses.push(...classes);
565
- Object.assign(allDynamicStyles, dynamicStyles);
566
201
  }
567
- return { classes: allClasses, dynamicStyles: allDynamicStyles };
568
- }
569
-
570
- // lib/server/astro/nodeToAstro.ts
571
- init_constants();
572
- function ind(ctx) {
573
- return " ".repeat(ctx.indent);
574
- }
575
- function isStyleMapping2(value) {
576
- return typeof value === "object" && value !== null && "_mapping" in value && value._mapping === true;
577
- }
578
- function isLinkMapping(value) {
579
- return typeof value === "object" && value !== null && "_mapping" in value && value._mapping === true;
580
- }
581
- function isHtmlMapping(value) {
582
- return typeof value === "object" && value !== null && "_mapping" in value && value._mapping === true;
583
- }
584
- function isResponsiveStyle2(style) {
585
- return "base" in style || "tablet" in style || "mobile" in style;
586
- }
587
- function escapeJSX(s) {
588
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
589
- }
590
- function escapeTemplateLiteral(s) {
591
- return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
202
+ return css;
592
203
  }
593
204
  function collectStyleMappings(style) {
594
205
  if (!style) return [];
595
206
  const result = [];
596
- if (isResponsiveStyle2(style)) {
597
- const responsive = style;
598
- for (const [bp, bpStyle] of Object.entries(responsive)) {
599
- if (!bpStyle) continue;
600
- for (const [prop, value] of Object.entries(bpStyle)) {
601
- if (isStyleMapping2(value)) {
602
- result.push({ property: prop, mapping: value, breakpoint: bp });
207
+ if (isResponsiveStyle(style)) {
208
+ const base = style.base;
209
+ if (base) {
210
+ for (const [prop, value] of Object.entries(base)) {
211
+ if (isStyleMapping(value)) {
212
+ result.push({ property: prop, mapping: value });
603
213
  }
604
214
  }
605
215
  }
606
216
  } else {
607
217
  for (const [prop, value] of Object.entries(style)) {
608
- if (isStyleMapping2(value)) {
218
+ if (isStyleMapping(value)) {
609
219
  result.push({ property: prop, mapping: value });
610
220
  }
611
221
  }
612
222
  }
613
223
  return result;
614
224
  }
615
- function mappingToClassListEntries(mapping, property, breakpointPrefix, ctx) {
616
- const entries = [];
617
- const values = Object.entries(mapping.values);
618
- if (values.length === 0) return entries;
619
- const propRef = ctx.isComponentDef ? mapping.prop : mapping.prop;
620
- if (values.length === 2) {
621
- const [[val1, css1], [val2, css2]] = values;
622
- const cls1 = getClassForValue(property, css1, breakpointPrefix);
623
- const cls2 = getClassForValue(property, css2, breakpointPrefix);
624
- if (cls1 && cls2) {
625
- entries.push(`${propRef} === ${JSON.stringify(coerceValue(val1))} ? '${cls1}' : '${cls2}'`);
626
- }
627
- } else {
628
- for (const [val, cssValue] of values) {
629
- const cls = getClassForValue(property, cssValue, breakpointPrefix);
630
- if (cls) {
631
- entries.push(`${propRef} === ${JSON.stringify(coerceValue(val))} && '${cls}'`);
225
+ function postfixToPseudoState(postfix) {
226
+ if (postfix.includes(":hover")) return "hover";
227
+ if (postfix.includes(":focus-visible")) return "focus-visible";
228
+ if (postfix.includes(":focus")) return "focus";
229
+ if (postfix.includes(":active")) return "active";
230
+ if (postfix.includes(":visited")) return "visited";
231
+ return null;
232
+ }
233
+ function mapStylesToWebflow(className, style, interactiveStyles, breakpoints) {
234
+ const webflowClassName = className.replace(/_/g, "-");
235
+ const primaryClass = {
236
+ name: webflowClassName,
237
+ base: {}
238
+ };
239
+ if (style) {
240
+ if (isResponsiveStyle(style)) {
241
+ const responsive = style;
242
+ if (responsive.base) {
243
+ primaryClass.base = styleObjectToCSS(responsive.base);
632
244
  }
633
- }
634
- }
635
- return entries;
636
- }
637
- function coerceValue(val) {
638
- if (val === "true") return true;
639
- if (val === "false") return false;
640
- return val;
641
- }
642
- function getClassForValue(property, value, breakpointPrefix) {
643
- const twClass = propertyToTailwind(property, value);
644
- if (!twClass) return null;
645
- return breakpointPrefix ? `${breakpointPrefix}${twClass}` : twClass;
646
- }
647
- function buildClassAndStyleExpression(style, interactiveStyles, elementClass, ctx) {
648
- const result = style ? responsiveStylesToTailwind(style, ctx.breakpoints) : { classes: [], dynamicStyles: {} };
649
- const staticClasses = result.classes;
650
- const dynamicStyles = result.dynamicStyles;
651
- if (elementClass) {
652
- staticClasses.unshift(elementClass);
653
- }
654
- const conditionals = [];
655
- const mappings = collectStyleMappings(style);
656
- for (const { property, mapping, breakpoint } of mappings) {
657
- const bpValue = breakpoint === "tablet" ? ctx.breakpoints.tablet?.breakpoint ?? 1024 : breakpoint === "mobile" ? ctx.breakpoints.mobile?.breakpoint ?? 540 : 0;
658
- const prefix = bpValue ? `max-[${bpValue}px]:` : "";
659
- const entries = mappingToClassListEntries(mapping, property, prefix, ctx);
660
- conditionals.push(...entries);
661
- }
662
- let styleAttr = "";
663
- if (Object.keys(dynamicStyles).length > 0 && ctx.isComponentDef) {
664
- const styleParts = [];
665
- for (const [cssProp, value] of Object.entries(dynamicStyles)) {
666
- const resolved = value.replace(/\{\{(.+?)\}\}/g, (_, expr) => `\${${expr.trim()}}`);
667
- styleParts.push(`${cssProp}: \${${resolved.includes("${") ? resolved.replace(/\$\{(.+?)\}/g, "$1") : `'${resolved}'`}}`);
668
- }
669
- const entries = [];
670
- for (const [cssProp, value] of Object.entries(dynamicStyles)) {
671
- const resolved = value.replace(/\{\{(.+?)\}\}/g, (_, expr) => `\${${expr.trim()}}`);
672
- entries.push(`${cssProp}: ${resolved}`);
673
- }
674
- styleAttr = ` style={\`${entries.join("; ")}\`}`;
675
- }
676
- if (interactiveStyles && interactiveStyles.length > 0 && ctx.isComponentDef && hasInteractiveStyleMappings(interactiveStyles)) {
677
- const { mappings: mappings2 } = extractInteractiveStyleMappings(interactiveStyles);
678
- if (mappings2.length > 0) {
679
- const varParts = [];
680
- for (const extracted of mappings2) {
681
- const { mapping, variableIndex } = extracted;
682
- const varName = `--is-${variableIndex}`;
683
- const entries = Object.entries(mapping.values);
684
- if (entries.length === 2) {
685
- const [[val1, css1], [val2, css2]] = entries;
686
- varParts.push(`'${varName}': ${mapping.prop} === ${JSON.stringify(coerceValue(val1))} ? '${css1}' : '${css2}'`);
687
- } else {
688
- const lookupEntries = entries.filter(([, v]) => v !== "").map(([k, v]) => `${JSON.stringify(coerceValue(k))}: '${v}'`).join(", ");
689
- varParts.push(`'${varName}': ({${lookupEntries}})[${mapping.prop}] || ''`);
690
- }
245
+ if (responsive.tablet) {
246
+ if (!primaryClass.breakpoints) primaryClass.breakpoints = {};
247
+ primaryClass.breakpoints.Tablet = styleObjectToCSS(responsive.tablet);
691
248
  }
692
- if (varParts.length > 0) {
693
- const existingStyleParts = styleAttr ? styleAttr.replace(/^ style=\{`/, "").replace(/`\}$/, "") : "";
694
- const varStyleExpr = varParts.join(", ");
695
- if (existingStyleParts) {
696
- styleAttr = ` style={\`${existingStyleParts}; \${ Object.entries({${varStyleExpr}}).map(([k,v]) => \`\${k}:\${v}\`).join(';') }\`}`;
697
- } else {
698
- styleAttr = ` style={Object.entries({${varStyleExpr}}).map(([k,v]) => \`\${k}:\${v}\`).join(';')}`;
249
+ if (responsive.mobile) {
250
+ if (!primaryClass.breakpoints) primaryClass.breakpoints = {};
251
+ primaryClass.breakpoints.MobilePortrait = styleObjectToCSS(responsive.mobile);
252
+ }
253
+ for (const [bpName, bpStyle] of Object.entries(responsive)) {
254
+ if (!bpStyle || bpName === "base" || bpName === "tablet" || bpName === "mobile") continue;
255
+ if (!primaryClass.breakpoints) primaryClass.breakpoints = {};
256
+ primaryClass.breakpoints.Tablet = {
257
+ ...primaryClass.breakpoints.Tablet,
258
+ ...styleObjectToCSS(bpStyle)
259
+ };
260
+ }
261
+ } else {
262
+ primaryClass.base = styleObjectToCSS(style);
263
+ }
264
+ }
265
+ if (interactiveStyles && interactiveStyles.length > 0) {
266
+ for (const rule of interactiveStyles) {
267
+ if (!rule.postfix) continue;
268
+ const pseudoState = postfixToPseudoState(rule.postfix);
269
+ if (!pseudoState) continue;
270
+ const ruleStyle = rule.style;
271
+ if (!primaryClass.pseudoStates) primaryClass.pseudoStates = {};
272
+ if (isResponsiveStyle(ruleStyle)) {
273
+ const responsive = ruleStyle;
274
+ if (responsive.base) {
275
+ primaryClass.pseudoStates[pseudoState] = {
276
+ ...primaryClass.pseudoStates[pseudoState],
277
+ ...styleObjectToCSS(responsive.base)
278
+ };
699
279
  }
280
+ } else {
281
+ primaryClass.pseudoStates[pseudoState] = {
282
+ ...primaryClass.pseudoStates[pseudoState],
283
+ ...styleObjectToCSS(ruleStyle)
284
+ };
700
285
  }
701
286
  }
702
287
  }
703
- let classExpr;
704
- if (conditionals.length === 0) {
705
- if (staticClasses.length === 0) {
706
- classExpr = "";
707
- } else {
708
- classExpr = ` class="${staticClasses.join(" ")}"`;
709
- }
710
- } else {
711
- const parts = [];
712
- if (staticClasses.length > 0) {
713
- parts.push(`'${staticClasses.join(" ")}'`);
288
+ const comboClasses = [];
289
+ const mappings = collectStyleMappings(style);
290
+ for (const { property, mapping } of mappings) {
291
+ for (const [value, cssValue] of Object.entries(mapping.values)) {
292
+ if (cssValue === "" || cssValue === void 0) continue;
293
+ const comboName = `is-${sanitizeClassName(mapping.prop)}-${sanitizeClassName(String(value))}`;
294
+ const comboClass = {
295
+ name: comboName,
296
+ base: {
297
+ [toKebabCase(property)]: typeof cssValue === "number" ? UNITLESS_PROPERTIES.has(toKebabCase(property)) ? String(cssValue) : `${cssValue}px` : String(cssValue)
298
+ },
299
+ comboParent: webflowClassName
300
+ };
301
+ comboClasses.push(comboClass);
714
302
  }
715
- parts.push(...conditionals);
716
- classExpr = ` class:list={[${parts.join(", ")}]}`;
717
- }
718
- return { classExpr, styleAttr };
719
- }
720
- function resolveTemplate(text, ctx) {
721
- if (!ctx.isComponentDef) {
722
- return text;
723
- }
724
- const fullMatch = text.match(/^\{\{(.+)\}\}$/);
725
- if (fullMatch) {
726
- return `{${fullMatch[1].trim()}}`;
727
303
  }
728
- return text.replace(/\{\{(.+?)\}\}/g, (_, expr) => `{${expr.trim()}}`);
304
+ return { primaryClass, comboClasses };
729
305
  }
730
- function hasTemplates(text) {
731
- return /\{\{.+?\}\}/.test(text);
306
+ function sanitizeClassName(name) {
307
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
732
308
  }
309
+
310
+ // lib/server/webflow/nodeToWebflow.ts
733
311
  function buildElementClass(ctx, label) {
734
312
  return generateElementClassName({
735
313
  fileType: ctx.fileType,
@@ -738,621 +316,272 @@ function buildElementClass(ctx, label) {
738
316
  path: ctx.elementPath
739
317
  });
740
318
  }
741
- function buildAttributesString(attributes, ctx) {
742
- if (!attributes) return "";
743
- const parts = [];
744
- for (const [key, value] of Object.entries(attributes)) {
745
- if (typeof value === "boolean") {
746
- if (value) parts.push(key);
747
- } else {
748
- const strVal = String(value);
749
- if (hasTemplates(strVal) && ctx.isComponentDef) {
750
- const fullMatch = strVal.match(/^\{\{(.+)\}\}$/);
751
- if (fullMatch) {
752
- parts.push(`${key}={${fullMatch[1].trim()}}`);
753
- } else {
754
- const resolved = strVal.replace(/\{\{(.+?)\}\}/g, (_, expr) => `\${${expr.trim()}}`);
755
- parts.push(`${key}={\`${resolved}\`}`);
756
- }
757
- } else {
758
- parts.push(`${key}="${escapeJSX(strVal)}"`);
759
- }
760
- }
319
+ function resolveTemplate(text, props) {
320
+ if (!props) return text;
321
+ return text.replace(/\{\{(.+?)\}\}/g, (_, expr) => {
322
+ const trimmed = expr.trim();
323
+ const orMatch = trimmed.match(/^(.+?)\s*\|\|\s*['"](.+?)['"]$/);
324
+ if (orMatch) {
325
+ const value2 = resolveNestedProp(props, orMatch[1].trim());
326
+ return value2 !== void 0 && value2 !== "" && value2 !== null ? String(value2) : orMatch[2];
327
+ }
328
+ const ternaryMatch = trimmed.match(/^(.+?)\s*\?\s*['"](.+?)['"]\s*:\s*['"](.+?)['"]$/);
329
+ if (ternaryMatch) {
330
+ const value2 = resolveNestedProp(props, ternaryMatch[1].trim());
331
+ return value2 ? ternaryMatch[2] : ternaryMatch[3];
332
+ }
333
+ const value = resolveNestedProp(props, trimmed);
334
+ return value !== void 0 ? String(value) : "";
335
+ });
336
+ }
337
+ function resolveNestedProp(obj, path) {
338
+ const parts = path.split(".");
339
+ let current = obj;
340
+ for (const part of parts) {
341
+ if (current === null || current === void 0 || typeof current !== "object") return void 0;
342
+ current = current[part];
761
343
  }
762
- return parts.length > 0 ? " " + parts.join(" ") : "";
344
+ return current;
763
345
  }
764
- function formatPropValue(value) {
765
- if (typeof value === "string") return `"${escapeJSX(value)}"`;
766
- if (typeof value === "number") return `{${value}}`;
767
- if (typeof value === "boolean") return `{${value}}`;
768
- if (value === null || value === void 0) return `{undefined}`;
769
- return `{${JSON.stringify(value)}}`;
346
+ function hasTemplates(text) {
347
+ return /\{\{.+?\}\}/.test(text);
770
348
  }
771
- function nodeToAstro(node, ctx) {
772
- if (node === null || node === void 0) return "";
349
+ function nodeToWebflow(node, ctx, instanceProps) {
350
+ if (node === null || node === void 0) return [];
773
351
  if (typeof node === "string") {
774
- if (hasTemplates(node) && ctx.isComponentDef) {
775
- return `${ind(ctx)}${resolveTemplate(node, ctx)}
776
- `;
777
- }
778
- return `${ind(ctx)}${escapeJSX(node)}
779
- `;
352
+ const text = instanceProps ? resolveTemplate(node, instanceProps) : node;
353
+ return [{ tag: "span", textContent: text }];
780
354
  }
781
355
  if (typeof node === "number") {
782
- return `${ind(ctx)}${node}
783
- `;
356
+ return [{ tag: "span", textContent: String(node) }];
784
357
  }
785
358
  if (Array.isArray(node)) {
786
- let result = "";
359
+ const results = [];
787
360
  for (let i = 0; i < node.length; i++) {
788
361
  const child = node[i];
789
362
  const savedPath = [...ctx.elementPath];
790
363
  ctx.elementPath = [...ctx.elementPath, i];
791
- result += nodeToAstro(child, ctx);
364
+ results.push(...nodeToWebflow(child, ctx, instanceProps));
792
365
  ctx.elementPath = savedPath;
793
366
  }
794
- return result;
367
+ return results;
795
368
  }
796
369
  switch (node.type) {
797
370
  case NODE_TYPE.NODE:
798
- return emitHtmlNode(node, ctx);
371
+ return [emitHtmlNode(node, ctx, instanceProps)];
799
372
  case NODE_TYPE.COMPONENT:
800
- return emitComponentInstance(node, ctx);
373
+ return emitComponentInstance(node, ctx, instanceProps);
801
374
  case NODE_TYPE.SLOT:
802
- return emitSlotMarker(node, ctx);
375
+ return emitSlotMarker(node, ctx, instanceProps);
803
376
  case NODE_TYPE.EMBED:
804
- return emitEmbedNode(node, ctx);
377
+ return [emitEmbedNode(node, ctx, instanceProps)];
805
378
  case NODE_TYPE.LINK:
806
- return emitLinkNode(node, ctx);
807
- case NODE_TYPE.LOCALE_LIST:
808
- return emitFallback(ctx);
379
+ return [emitLinkNode(node, ctx, instanceProps)];
809
380
  case NODE_TYPE.LIST:
810
381
  case "cms-list":
811
- return emitFallback(ctx);
382
+ case NODE_TYPE.LOCALE_LIST:
383
+ return [{ tag: "div", attributes: { "data-meno-type": node.type } }];
812
384
  default:
813
- return emitFallback(ctx);
385
+ return [];
814
386
  }
815
387
  }
816
- function emitHtmlNode(node, ctx) {
817
- let tag = node.tag;
818
- const label = node.label;
388
+ function emitHtmlNode(node, ctx, instanceProps) {
389
+ const tag = hasTemplates(node.tag) && instanceProps ? resolveTemplate(node.tag, instanceProps) : node.tag;
819
390
  const style = node.style;
820
- let isDynamic = false;
821
- let dynamicTagVar = "";
822
- if (hasTemplates(tag) && ctx.isComponentDef) {
823
- isDynamic = true;
824
- dynamicTagVar = `Tag_${ctx.elementPath.join("_")}`;
825
- const resolved = tag.replace(/\{\{(.+?)\}\}/g, (_, expr) => `\${${expr.trim()}}`);
826
- if (!ctx.dynamicTags) ctx.dynamicTags = /* @__PURE__ */ new Map();
827
- ctx.dynamicTags.set(dynamicTagVar, resolved);
828
- tag = dynamicTagVar;
829
- }
830
- const ifExpr = emitIfOpen(node, ctx);
831
- let elementClass = null;
832
- if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
833
- elementClass = buildElementClass(ctx, label);
834
- }
835
- const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles, elementClass, ctx);
836
- const attrs = buildAttributesString(node.attributes, ctx);
837
- const openClose = isDynamic ? dynamicTagVar : tag;
838
- if (!isDynamic && isVoidElement(tag)) {
839
- return `${ifExpr}${ind(ctx)}<${tag}${classExpr}${styleAttr}${attrs} />
840
- ${emitIfClose(node, ctx)}`;
841
- }
842
- const children = emitChildren(node.children, ctx);
843
- if (!children.trim() && !isDynamic) {
844
- return `${ifExpr}${ind(ctx)}<${tag}${classExpr}${styleAttr}${attrs} />
845
- ${emitIfClose(node, ctx)}`;
846
- }
847
- return `${ifExpr}${ind(ctx)}<${openClose}${classExpr}${styleAttr}${attrs}>
848
- ` + children + `${ind(ctx)}</${openClose}>
849
- ${emitIfClose(node, ctx)}`;
850
- }
851
- function emitComponentInstance(node, ctx) {
852
- const name = node.component;
853
- ctx.imports.add(name);
854
- const ifExpr = emitIfOpen(node, ctx);
855
- const propParts = [];
856
- if (node.props) {
857
- for (const [key, value] of Object.entries(node.props)) {
858
- if (key === "children") continue;
859
- if (typeof value === "string" && hasTemplates(value) && ctx.isComponentDef) {
860
- const fullMatch = value.match(/^\{\{(.+)\}\}$/);
861
- if (fullMatch) {
862
- propParts.push(`${key}={${fullMatch[1].trim()}}`);
863
- } else {
864
- const resolved = value.replace(/\{\{(.+?)\}\}/g, (_, expr) => `\${${expr.trim()}}`);
865
- propParts.push(`${key}={\`${resolved}\`}`);
866
- }
867
- } else {
868
- propParts.push(`${key}=${formatPropValue(value)}`);
391
+ const interactiveStyles = node.interactiveStyles;
392
+ const needsClass = style || interactiveStyles && interactiveStyles.length > 0 || node.generateElementClass;
393
+ let className;
394
+ let comboClassNames;
395
+ if (needsClass) {
396
+ const elementClass = buildElementClass(ctx, node.label);
397
+ const { primaryClass, comboClasses } = mapStylesToWebflow(
398
+ elementClass,
399
+ style,
400
+ interactiveStyles,
401
+ ctx.breakpoints
402
+ );
403
+ className = primaryClass.name;
404
+ ctx.styleClasses.set(primaryClass.name, primaryClass);
405
+ if (comboClasses.length > 0) {
406
+ comboClassNames = [];
407
+ for (const combo of comboClasses) {
408
+ ctx.styleClasses.set(combo.name, combo);
409
+ comboClassNames.push(combo.name);
869
410
  }
870
411
  }
871
412
  }
872
- if (node.style) {
873
- const { classes: instanceClasses } = responsiveStylesToTailwind(node.style, ctx.breakpoints);
874
- if (instanceClasses.length > 0) {
875
- propParts.push(`class="${instanceClasses.join(" ")}"`);
876
- }
877
- }
878
- const propsStr = propParts.length > 0 ? " " + propParts.join(" ") : "";
879
- const children = emitChildren(node.children, ctx);
880
- if (!children.trim()) {
881
- return `${ifExpr}${ind(ctx)}<${name}${propsStr} />
882
- ${emitIfClose(node, ctx)}`;
883
- }
884
- return `${ifExpr}${ind(ctx)}<${name}${propsStr}>
885
- ` + children + `${ind(ctx)}</${name}>
886
- ${emitIfClose(node, ctx)}`;
887
- }
888
- function emitSlotMarker(node, ctx) {
889
- if (node.default) {
890
- const defaultContent = emitChildren(node.default, ctx);
891
- if (defaultContent.trim()) {
892
- return `${ind(ctx)}<slot>
893
- ` + defaultContent + `${ind(ctx)}</slot>
894
- `;
895
- }
896
- }
897
- return `${ind(ctx)}<slot />
898
- `;
899
- }
900
- function emitEmbedNode(node, ctx) {
901
- const ifExpr = emitIfOpen(node, ctx);
902
- const style = node.style;
903
- let elementClass = null;
904
- if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
905
- elementClass = buildElementClass(ctx, node.label);
906
- }
907
- const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles, elementClass, ctx);
908
- const attrs = buildAttributesString(node.attributes, ctx);
909
- if (isHtmlMapping(node.html)) {
910
- if (ctx.isComponentDef) {
911
- const propRef = node.html.prop;
912
- return `${ifExpr}${ind(ctx)}<div${classExpr.replace('"', '"oem ') || ' class="oem"'}${attrs}>
913
- ${ind(ctx)} <Fragment set:html={${propRef}} />
914
- ${ind(ctx)}</div>
915
- ${emitIfClose(node, ctx)}`;
413
+ const attributes = {};
414
+ if (node.attributes) {
415
+ for (const [key, value] of Object.entries(node.attributes)) {
416
+ if (instanceProps && typeof value === "string" && hasTemplates(value)) {
417
+ attributes[key] = resolveTemplate(value, instanceProps);
418
+ } else {
419
+ attributes[key] = value;
420
+ }
916
421
  }
917
422
  }
918
- const htmlStr = typeof node.html === "string" ? node.html : "";
919
- const escapedHtml = escapeTemplateLiteral(htmlStr);
920
- let finalClassExpr = classExpr;
921
- if (!classExpr.includes("oem")) {
922
- if (classExpr) {
923
- finalClassExpr = classExpr.replace(/class="/, 'class="oem ').replace(/class:list={\['/, "class:list={['oem ");
423
+ let children;
424
+ let textContent;
425
+ if (!isVoidElement(tag) && node.children) {
426
+ if (typeof node.children === "string") {
427
+ textContent = instanceProps ? resolveTemplate(node.children, instanceProps) : node.children;
428
+ } else if (Array.isArray(node.children) && node.children.length === 1 && typeof node.children[0] === "string") {
429
+ const text = node.children[0];
430
+ textContent = instanceProps ? resolveTemplate(text, instanceProps) : text;
924
431
  } else {
925
- finalClassExpr = ' class="oem"';
432
+ const innerCtx = { ...ctx, elementPath: [...ctx.elementPath] };
433
+ children = convertChildren(node.children, innerCtx, instanceProps);
434
+ if (children.length === 0) children = void 0;
926
435
  }
927
436
  }
928
- return `${ifExpr}${ind(ctx)}<div${finalClassExpr}${attrs}>
929
- ${ind(ctx)} <Fragment set:html={\`${escapedHtml}\`} />
930
- ${ind(ctx)}</div>
931
- ${emitIfClose(node, ctx)}`;
932
- }
933
- function emitLinkNode(node, ctx) {
934
- const ifExpr = emitIfOpen(node, ctx);
935
- const style = node.style;
936
- let elementClass = null;
937
- if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
938
- elementClass = buildElementClass(ctx, node.label);
939
- }
940
- const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles, elementClass, ctx);
941
- let finalClassExpr = classExpr;
942
- if (!classExpr.includes("olink")) {
943
- if (classExpr) {
944
- finalClassExpr = classExpr.replace(/class="/, 'class="olink ').replace(/class:list={\['/, "class:list={['olink ");
945
- } else {
946
- finalClassExpr = ' class="olink"';
437
+ let conditional;
438
+ const ifValue = node.if;
439
+ if (ifValue !== void 0 && ifValue !== true) {
440
+ if (typeof ifValue === "object" && ifValue._mapping) {
441
+ conditional = { prop: ifValue.prop, condition: "truthy" };
442
+ } else if (typeof ifValue === "string") {
443
+ const match = ifValue.match(/^\{\{(.+)\}\}$/);
444
+ conditional = { prop: match ? match[1].trim() : ifValue, condition: "truthy" };
445
+ }
446
+ }
447
+ const element = { tag };
448
+ if (className) element.className = className;
449
+ if (comboClassNames) element.comboClasses = comboClassNames;
450
+ if (textContent) element.textContent = textContent;
451
+ if (children) element.children = children;
452
+ if (Object.keys(attributes).length > 0) element.attributes = attributes;
453
+ if (conditional) element.conditional = conditional;
454
+ return element;
455
+ }
456
+ function emitComponentInstance(node, ctx, parentProps) {
457
+ const compDef = ctx.globalComponents[node.component];
458
+ if (!compDef) {
459
+ return [{ tag: "div", attributes: { "data-component": node.component } }];
460
+ }
461
+ const resolvedProps = {};
462
+ const structured = compDef.component;
463
+ if (structured?.interface) {
464
+ for (const [key, propDef] of Object.entries(structured.interface)) {
465
+ resolvedProps[key] = propDef.default;
947
466
  }
948
467
  }
949
- let hrefAttr;
950
- if (isLinkMapping(node.href)) {
951
- if (ctx.isComponentDef) {
952
- const propRef = node.href.prop;
953
- hrefAttr = ` href={${propRef}.href}`;
954
- } else {
955
- hrefAttr = ' href="#"';
956
- }
957
- } else {
958
- const href = typeof node.href === "string" ? node.href : "#";
959
- if (hasTemplates(href) && ctx.isComponentDef) {
960
- const fullMatch = href.match(/^\{\{(.+)\}\}$/);
961
- if (fullMatch) {
962
- hrefAttr = ` href={${fullMatch[1].trim()}}`;
468
+ if (node.props) {
469
+ for (const [key, value] of Object.entries(node.props)) {
470
+ if (key === "children") continue;
471
+ if (typeof value === "string" && hasTemplates(value) && parentProps) {
472
+ resolvedProps[key] = resolveTemplate(value, parentProps);
963
473
  } else {
964
- const resolved = href.replace(/\{\{(.+?)\}\}/g, (_, expr) => `\${${expr.trim()}}`);
965
- hrefAttr = ` href={\`${resolved}\`}`;
474
+ resolvedProps[key] = value;
966
475
  }
967
- } else {
968
- hrefAttr = ` href="${escapeJSX(href)}"`;
969
476
  }
970
477
  }
971
- const attrs = buildAttributesString(node.attributes, ctx);
972
- const children = emitChildren(node.children, ctx);
973
- if (!children.trim()) {
974
- return `${ifExpr}${ind(ctx)}<a${hrefAttr}${finalClassExpr}${styleAttr}${attrs} />
975
- ${emitIfClose(node, ctx)}`;
976
- }
977
- return `${ifExpr}${ind(ctx)}<a${hrefAttr}${finalClassExpr}${styleAttr}${attrs}>
978
- ` + children + `${ind(ctx)}</a>
979
- ${emitIfClose(node, ctx)}`;
980
- }
981
- function emitFallback(ctx) {
982
- const pathKey = ctx.elementPath.join(".");
983
- const ssrHtml = ctx.ssrFallbacks.get(pathKey);
984
- if (ssrHtml) {
985
- const escaped = escapeTemplateLiteral(ssrHtml);
986
- return `${ind(ctx)}<Fragment set:html={\`${escaped}\`} />
987
- `;
988
- }
989
- return `${ind(ctx)}{/* Complex node - SSR fallback not available */}
990
- `;
991
- }
992
- function emitIfOpen(node, ctx) {
993
- const ifValue = node.if;
994
- if (ifValue === void 0 || ifValue === true) return "";
995
- if (typeof ifValue === "boolean") {
996
- return ifValue ? "" : `${ind(ctx)}{/* hidden */}
997
- `;
998
- }
999
- if (typeof ifValue === "object" && ifValue._mapping && ctx.isComponentDef) {
1000
- return `${ind(ctx)}{${ifValue.prop} && (
1001
- `;
1002
- }
1003
- if (typeof ifValue === "string" && ctx.isComponentDef) {
1004
- const fullMatch = ifValue.match(/^\{\{(.+)\}\}$/);
1005
- const expr = fullMatch ? fullMatch[1].trim() : ifValue.replace(/\{\{(.+?)\}\}/g, "$1");
1006
- return `${ind(ctx)}{${expr} && (
1007
- `;
1008
- }
1009
- return "";
1010
- }
1011
- function emitIfClose(node, ctx) {
1012
- const ifValue = node.if;
1013
- if (ifValue === void 0 || ifValue === true) return "";
1014
- if (typeof ifValue === "boolean") return "";
1015
- if (typeof ifValue === "object" && ifValue._mapping && ctx.isComponentDef || typeof ifValue === "string" && ctx.isComponentDef) {
1016
- return `${ind(ctx)})}
1017
- `;
1018
- }
1019
- return "";
1020
- }
1021
- function emitChildren(children, ctx) {
1022
- if (!children) return "";
1023
- const innerCtx = { ...ctx, indent: ctx.indent + 1, elementPath: [...ctx.elementPath] };
1024
- if (typeof children === "string") {
1025
- return nodeToAstro(children, innerCtx);
1026
- }
1027
- if (Array.isArray(children)) {
1028
- return nodeToAstro(children, innerCtx);
1029
- }
1030
- return nodeToAstro(children, innerCtx);
1031
- }
1032
-
1033
- // lib/server/astro/componentEmitter.ts
1034
- function propDefToTSType(def) {
1035
- switch (def.type) {
1036
- case "string":
1037
- case "rich-text":
1038
- case "file":
1039
- return "string";
1040
- case "number":
1041
- return "number";
1042
- case "boolean":
1043
- return "boolean";
1044
- case "select":
1045
- if ("options" in def && def.options && def.options.length > 0) {
1046
- return def.options.map((o) => `'${o}'`).join(" | ");
1047
- }
1048
- return "string";
1049
- case "link":
1050
- return "{ href: string; target?: string }";
1051
- case "list":
1052
- return "any[]";
1053
- default:
1054
- return "any";
1055
- }
1056
- }
1057
- function formatDefault(def) {
1058
- if (!("default" in def) || def.default === void 0) return null;
1059
- const val = def.default;
1060
- if (typeof val === "string") return JSON.stringify(val);
1061
- if (typeof val === "number" || typeof val === "boolean") return String(val);
1062
- if (typeof val === "object" && val !== null && "_i18n" in val) {
1063
- for (const [key, v] of Object.entries(val)) {
1064
- if (key !== "_i18n" && typeof v === "string") return JSON.stringify(v);
1065
- }
1066
- return null;
1067
- }
1068
- if (typeof val === "object" && val !== null && "href" in val) {
1069
- return JSON.stringify(val);
1070
- }
1071
- if (Array.isArray(val)) {
1072
- return JSON.stringify(val);
1073
- }
1074
- return JSON.stringify(val);
1075
- }
1076
- function emitAstroComponent(name, def, allComponents, breakpoints = DEFAULT_BREAKPOINTS) {
1077
- const comp = def.component;
1078
- const propDefs = comp.interface || {};
1079
- const structure = comp.structure;
1080
- if (!structure) {
1081
- return buildNoStructureComponent(name, comp);
1082
- }
1083
- const ctx = {
1084
- imports: /* @__PURE__ */ new Set(),
1085
- isComponentDef: true,
1086
- componentProps: propDefs,
1087
- globalComponents: allComponents,
1088
- indent: 0,
1089
- ssrFallbacks: /* @__PURE__ */ new Map(),
1090
- elementPath: [0],
478
+ const body = structured?.structure || compDef.node;
479
+ if (!body) return [];
480
+ const compCtx = {
481
+ ...ctx,
1091
482
  fileType: "component",
1092
- fileName: name,
1093
- breakpoints
483
+ fileName: node.component,
484
+ elementPath: [0],
485
+ slotChildren: node.children
1094
486
  };
1095
- const templateBody = nodeToAstro(structure, ctx);
1096
- const frontmatter = buildFrontmatter(name, propDefs, ctx.imports, ctx.dynamicTags);
1097
- const styleSection = comp.css ? `
1098
- <style>
1099
- ${comp.css}
1100
- </style>
1101
- ` : "";
1102
- const scriptSection = comp.javascript ? `
1103
- <script>
1104
- ${comp.javascript}
1105
- </script>
1106
- ` : "";
1107
- return `---
1108
- ${frontmatter}---
1109
- ${templateBody}${styleSection}${scriptSection}`;
487
+ return nodeToWebflow(body, compCtx, resolvedProps);
1110
488
  }
1111
- function buildFrontmatter(componentName, propDefs, imports, dynamicTags) {
1112
- const lines = [];
1113
- for (const imp of Array.from(imports).sort()) {
1114
- lines.push(`import ${imp} from './${imp}.astro';`);
1115
- }
1116
- if (lines.length > 0) lines.push("");
1117
- const propEntries = Object.entries(propDefs);
1118
- if (propEntries.length > 0) {
1119
- lines.push("interface Props {");
1120
- for (const [propName, propDef] of propEntries) {
1121
- if (propName === "children") continue;
1122
- const tsType = propDefToTSType(propDef);
1123
- const optional = "default" in propDef && propDef.default !== void 0;
1124
- lines.push(` ${propName}${optional ? "?" : ""}: ${tsType};`);
1125
- }
1126
- lines.push("}");
1127
- lines.push("");
1128
- const destructParts = [];
1129
- for (const [propName, propDef] of propEntries) {
1130
- if (propName === "children") continue;
1131
- const defaultVal = formatDefault(propDef);
1132
- if (defaultVal !== null) {
1133
- destructParts.push(`${propName} = ${defaultVal}`);
1134
- } else {
1135
- destructParts.push(propName);
1136
- }
1137
- }
1138
- if (destructParts.length > 0) {
1139
- if (destructParts.length <= 3 && destructParts.join(", ").length < 80) {
1140
- lines.push(`const { ${destructParts.join(", ")} } = Astro.props;`);
1141
- } else {
1142
- lines.push("const {");
1143
- for (const part of destructParts) {
1144
- lines.push(` ${part},`);
1145
- }
1146
- lines.push("} = Astro.props;");
1147
- }
1148
- }
489
+ function emitSlotMarker(node, ctx, instanceProps) {
490
+ if (ctx.slotChildren) {
491
+ const parentCtx = {
492
+ ...ctx,
493
+ slotChildren: void 0
494
+ // prevent infinite slot nesting
495
+ };
496
+ return convertChildren(ctx.slotChildren, parentCtx, instanceProps);
1149
497
  }
1150
- if (dynamicTags && dynamicTags.size > 0) {
1151
- lines.push("");
1152
- for (const [varName, templateExpr] of dynamicTags) {
1153
- lines.push(`const ${varName} = \`${templateExpr}\`;`);
1154
- }
498
+ if (node.default) {
499
+ return convertChildren(node.default, ctx, instanceProps);
1155
500
  }
1156
- if (lines.length > 0) lines.push("");
1157
- return lines.join("\n");
1158
- }
1159
- function buildNoStructureComponent(name, comp) {
1160
- let content = "---\n---\n<slot />\n";
1161
- if (comp.css) content += `
1162
- <style>
1163
- ${comp.css}
1164
- </style>
1165
- `;
1166
- if (comp.javascript) content += `
1167
- <script>
1168
- ${comp.javascript}
1169
- </script>
1170
- `;
1171
- return content;
1172
- }
1173
-
1174
- // lib/server/astro/pageEmitter.ts
1175
- function escapeTemplateLiteral2(s) {
1176
- return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
501
+ return [];
1177
502
  }
1178
- function escapeJSX2(s) {
1179
- return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
1180
- }
1181
- function componentImportPath(fileDepth, componentName) {
1182
- const ups = "../".repeat(fileDepth + 1);
1183
- return `${ups}components/${componentName}.astro`;
1184
- }
1185
- function emitAstroPage(options) {
1186
- const {
1187
- pageData,
1188
- globalComponents,
1189
- title,
1190
- meta,
1191
- locale,
1192
- theme,
1193
- fontPreloads,
1194
- libraryTags,
1195
- scriptPaths,
1196
- layoutImportPath: layoutImportPath2,
1197
- fileDepth,
1198
- ssrFallbacks,
1199
- pageName,
1200
- breakpoints: breakpointsOpt
1201
- } = options;
1202
- const breakpoints = breakpointsOpt ?? DEFAULT_BREAKPOINTS;
1203
- const root = pageData.root;
1204
- if (!root) {
1205
- return buildEmptyPage(layoutImportPath2, title, meta, locale, theme, fontPreloads, libraryTags, scriptPaths);
503
+ function emitEmbedNode(node, ctx, instanceProps) {
504
+ const style = node.style;
505
+ const interactiveStyles = node.interactiveStyles;
506
+ let className;
507
+ if (style || interactiveStyles && interactiveStyles.length > 0) {
508
+ const elementClass = buildElementClass(ctx, node.label);
509
+ const { primaryClass } = mapStylesToWebflow(
510
+ elementClass,
511
+ style,
512
+ interactiveStyles,
513
+ ctx.breakpoints
514
+ );
515
+ className = primaryClass.name;
516
+ ctx.styleClasses.set(primaryClass.name, primaryClass);
1206
517
  }
1207
- const ctx = {
1208
- imports: /* @__PURE__ */ new Set(),
1209
- isComponentDef: false,
1210
- componentProps: {},
1211
- globalComponents,
1212
- indent: 1,
1213
- // inside BaseLayout
1214
- ssrFallbacks,
1215
- elementPath: [0],
1216
- fileType: "page",
1217
- fileName: pageName,
1218
- breakpoints
518
+ const htmlStr = typeof node.html === "string" ? node.html : "";
519
+ const element = {
520
+ tag: "div",
521
+ rawHtml: htmlStr
1219
522
  };
1220
- const templateBody = nodeToAstro(root, ctx);
1221
- const importLines = [];
1222
- importLines.push(`import BaseLayout from '${layoutImportPath2}';`);
1223
- const componentImports = Array.from(ctx.imports).sort();
1224
- for (const comp of componentImports) {
1225
- const path = componentImportPath(fileDepth, comp);
1226
- importLines.push(`import ${comp} from '${path}';`);
1227
- }
1228
- const scriptsArrayLiteral = scriptPaths.length > 0 ? `[${scriptPaths.map((s) => `"${s}"`).join(", ")}]` : "[]";
1229
- const libraryTagsLiteral = `{ headCSS: \`${escapeTemplateLiteral2(libraryTags.headCSS || "")}\`, headJS: \`${escapeTemplateLiteral2(libraryTags.headJS || "")}\`, bodyEndJS: \`${escapeTemplateLiteral2(libraryTags.bodyEndJS || "")}\` }`;
1230
- const escapedMeta = escapeTemplateLiteral2(meta);
1231
- const escapedFontPreloads = escapeTemplateLiteral2(fontPreloads);
1232
- return `---
1233
- ${importLines.join("\n")}
1234
- ---
1235
- <BaseLayout
1236
- title="${escapeJSX2(title)}"
1237
- meta={\`${escapedMeta}\`}
1238
- scripts={${scriptsArrayLiteral}}
1239
- locale="${locale}"
1240
- theme="${theme}"
1241
- fontPreloads={\`${escapedFontPreloads}\`}
1242
- libraryTags={${libraryTagsLiteral}}
1243
- >
1244
- ${templateBody}</BaseLayout>
1245
- `;
1246
- }
1247
- function buildEmptyPage(layoutImport, title, meta, locale, theme, fontPreloads, libraryTags, scriptPaths) {
1248
- const escapedMeta = escapeTemplateLiteral2(meta);
1249
- const escapedFontPreloads = escapeTemplateLiteral2(fontPreloads);
1250
- const scriptsArrayLiteral = scriptPaths.length > 0 ? `[${scriptPaths.map((s) => `"${s}"`).join(", ")}]` : "[]";
1251
- const libraryTagsLiteral = `{ headCSS: \`${escapeTemplateLiteral2(libraryTags.headCSS || "")}\`, headJS: \`${escapeTemplateLiteral2(libraryTags.headJS || "")}\`, bodyEndJS: \`${escapeTemplateLiteral2(libraryTags.bodyEndJS || "")}\` }`;
1252
- return `---
1253
- import BaseLayout from '${layoutImport}';
1254
- ---
1255
- <BaseLayout
1256
- title="${escapeJSX2(title)}"
1257
- meta={\`${escapedMeta}\`}
1258
- scripts={${scriptsArrayLiteral}}
1259
- locale="${locale}"
1260
- theme="${theme}"
1261
- fontPreloads={\`${escapedFontPreloads}\`}
1262
- libraryTags={${libraryTagsLiteral}}
1263
- >
1264
- </BaseLayout>
1265
- `;
1266
- }
1267
-
1268
- // lib/server/astro/cssCollector.ts
1269
- function isStyleMapping3(value) {
1270
- return typeof value === "object" && value !== null && "_mapping" in value && value._mapping === true;
523
+ if (className) element.className = className;
524
+ return element;
1271
525
  }
1272
- function isResponsiveStyle3(style) {
1273
- return "base" in style || "tablet" in style || "mobile" in style;
1274
- }
1275
- function collectFromStyle(style, classes, breakpoints) {
1276
- if (!style) return;
1277
- if (isResponsiveStyle3(style)) {
1278
- for (const [bp, bpStyle] of Object.entries(style)) {
1279
- if (!bpStyle) continue;
1280
- let prefix = "";
1281
- if (bp !== "base") {
1282
- const bpValue = breakpoints[bp]?.breakpoint;
1283
- if (bpValue) {
1284
- prefix = `max-[${bpValue}px]:`;
1285
- }
1286
- }
1287
- collectFromFlatStyle(bpStyle, prefix, classes);
1288
- }
1289
- } else {
1290
- collectFromFlatStyle(style, "", classes);
1291
- }
1292
- }
1293
- function collectFromFlatStyle(style, prefix, classes) {
1294
- for (const [property, value] of Object.entries(style)) {
1295
- if (!isStyleMapping3(value)) continue;
1296
- for (const [, cssValue] of Object.entries(value.values)) {
1297
- const twClass = propertyToTailwind(property, cssValue);
1298
- if (twClass) {
1299
- classes.add(prefix ? `${prefix}${twClass}` : twClass);
1300
- }
1301
- }
1302
- }
1303
- }
1304
- function walkNode(node, classes, breakpoints) {
1305
- if (!node || typeof node === "string" || typeof node === "number") return;
1306
- if (Array.isArray(node)) {
1307
- for (const child of node) {
1308
- walkNode(child, classes, breakpoints);
1309
- }
1310
- return;
1311
- }
1312
- if ("style" in node && node.style) {
1313
- collectFromStyle(node.style, classes, breakpoints);
1314
- }
1315
- if ("interactiveStyles" in node && Array.isArray(node.interactiveStyles)) {
1316
- for (const rule of node.interactiveStyles) {
1317
- if (rule.style) {
1318
- collectFromStyle(rule.style, classes, breakpoints);
1319
- }
1320
- }
1321
- }
1322
- if ("children" in node && node.children) {
1323
- if (Array.isArray(node.children)) {
1324
- for (const child of node.children) {
1325
- walkNode(child, classes, breakpoints);
1326
- }
1327
- }
526
+ function emitLinkNode(node, ctx, instanceProps) {
527
+ const style = node.style;
528
+ const interactiveStyles = node.interactiveStyles;
529
+ let className;
530
+ if (style || interactiveStyles && interactiveStyles.length > 0) {
531
+ const elementClass = buildElementClass(ctx, node.label);
532
+ const { primaryClass } = mapStylesToWebflow(
533
+ elementClass,
534
+ style,
535
+ interactiveStyles,
536
+ ctx.breakpoints
537
+ );
538
+ className = primaryClass.name;
539
+ ctx.styleClasses.set(primaryClass.name, primaryClass);
540
+ }
541
+ let href = "#";
542
+ if (typeof node.href === "string") {
543
+ href = instanceProps && hasTemplates(node.href) ? resolveTemplate(node.href, instanceProps) : node.href;
544
+ }
545
+ const attributes = { href };
546
+ if (node.attributes) {
547
+ for (const [key, value] of Object.entries(node.attributes)) {
548
+ attributes[key] = value;
549
+ }
550
+ }
551
+ let children;
552
+ if (node.children) {
553
+ children = convertChildren(node.children, ctx, instanceProps);
554
+ if (children.length === 0) children = void 0;
555
+ }
556
+ const element = { tag: "a", attributes };
557
+ if (className) element.className = className;
558
+ if (children) element.children = children;
559
+ return element;
560
+ }
561
+ function convertChildren(children, ctx, instanceProps) {
562
+ if (!children) return [];
563
+ if (typeof children === "string") {
564
+ return nodeToWebflow(children, ctx, instanceProps);
1328
565
  }
1329
- }
1330
- function collectAllMappingClasses(componentDefs, breakpoints = DEFAULT_BREAKPOINTS) {
1331
- const classes = /* @__PURE__ */ new Set();
1332
- for (const def of Object.values(componentDefs)) {
1333
- const structure = def.component?.structure;
1334
- if (structure) {
1335
- walkNode(structure, classes, breakpoints);
1336
- }
566
+ if (Array.isArray(children)) {
567
+ return nodeToWebflow(children, ctx, instanceProps);
1337
568
  }
1338
- return classes;
569
+ return nodeToWebflow(children, ctx, instanceProps);
1339
570
  }
1340
571
 
1341
- // build-astro.ts
1342
- function hashContent2(content) {
1343
- return createHash("sha256").update(content).digest("hex").slice(0, 8);
1344
- }
1345
- function copyDirectory(src, dest) {
1346
- if (!existsSync(src)) return;
1347
- if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
1348
- const files = readdirSync(src);
1349
- for (const file of files) {
1350
- const srcPath = join(src, file);
1351
- const destPath = join(dest, file);
1352
- const stat = statSync(srcPath);
1353
- if (stat.isDirectory()) copyDirectory(srcPath, destPath);
1354
- else copyFileSync(srcPath, destPath);
572
+ // lib/server/webflow/buildWebflow.ts
573
+ function scanJSONFiles(dir, prefix = "") {
574
+ const results = [];
575
+ if (!existsSync(dir)) return results;
576
+ const entries = readdirSync(dir, { withFileTypes: true });
577
+ for (const entry of entries) {
578
+ if (entry.isFile() && entry.name.endsWith(".json")) {
579
+ results.push(prefix ? `${prefix}/${entry.name}` : entry.name);
580
+ } else if (entry.isDirectory()) {
581
+ results.push(...scanJSONFiles(join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name));
582
+ }
1355
583
  }
584
+ return results;
1356
585
  }
1357
586
  function isCMSPage(pageData) {
1358
587
  return pageData.meta?.source === "cms" && !!pageData.meta?.cms;
@@ -1364,132 +593,81 @@ function buildCMSItemPath(urlPattern, item, slugField, locale, i18nConfig) {
1364
593
  }
1365
594
  return urlPattern.replace("{{slug}}", String(slug));
1366
595
  }
1367
- function scanJSONFiles(dir, prefix = "") {
596
+ function scanAssets(projectRoot) {
597
+ const assets = [];
598
+ const assetDirs = [
599
+ { dir: "images", type: "image" },
600
+ { dir: "fonts", type: "font" },
601
+ { dir: "videos", type: "video" },
602
+ { dir: "assets", type: "file" }
603
+ ];
604
+ for (const { dir, type } of assetDirs) {
605
+ const fullDir = join(projectRoot, dir);
606
+ if (!existsSync(fullDir)) continue;
607
+ const files = scanJSONFiles(fullDir).map((f) => f.replace(".json", ""));
608
+ const allFiles = scanAllFiles(fullDir);
609
+ for (const file of allFiles) {
610
+ assets.push({
611
+ localPath: `${dir}/${file}`,
612
+ type,
613
+ fileName: file.split("/").pop()
614
+ });
615
+ }
616
+ }
617
+ return assets;
618
+ }
619
+ function scanAllFiles(dir, prefix = "") {
1368
620
  const results = [];
1369
621
  if (!existsSync(dir)) return results;
1370
622
  const entries = readdirSync(dir, { withFileTypes: true });
1371
623
  for (const entry of entries) {
1372
- if (entry.isFile() && entry.name.endsWith(".json")) {
1373
- results.push(prefix ? `${prefix}/${entry.name}` : entry.name);
624
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
625
+ if (entry.isFile()) {
626
+ results.push(relativePath);
1374
627
  } else if (entry.isDirectory()) {
1375
- results.push(...scanJSONFiles(join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name));
628
+ results.push(...scanAllFiles(join(dir, entry.name), relativePath));
1376
629
  }
1377
630
  }
1378
631
  return results;
1379
632
  }
1380
- function layoutImportPath(fileDepth) {
1381
- const ups = "../".repeat(fileDepth + 1);
1382
- return `${ups}layouts/BaseLayout.astro`;
1383
- }
1384
- function escapeTemplateLiteral3(s) {
1385
- return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
1386
- }
1387
- function cmsFieldToZod(field) {
1388
- switch (field.type) {
1389
- case "string":
1390
- case "text":
1391
- case "rich-text":
1392
- return "z.string()";
1393
- case "number":
1394
- return "z.number()";
1395
- case "boolean":
1396
- return "z.boolean()";
1397
- case "date":
1398
- return "z.coerce.date()";
1399
- case "select":
1400
- if (field.options && field.options.length > 0) {
1401
- const opts = field.options.map((o) => `'${o.replace(/'/g, "\\'")}'`).join(", ");
1402
- return `z.enum([${opts}])`;
1403
- }
1404
- return "z.string()";
1405
- case "image":
1406
- case "file":
1407
- return "z.string()";
1408
- case "reference":
1409
- return "z.string()";
1410
- default:
1411
- return "z.string()";
633
+ function extractCSSVariables(themeColorCSS, variablesCSS) {
634
+ const vars = {};
635
+ const regex = /--([\w-]+)\s*:\s*([^;]+)/g;
636
+ for (const css of [themeColorCSS, variablesCSS]) {
637
+ let match;
638
+ while ((match = regex.exec(css)) !== null) {
639
+ vars[`--${match[1]}`] = match[2].trim();
640
+ }
1412
641
  }
642
+ return vars;
1413
643
  }
1414
- function buildSSRFallbackPage(result, importPath, fontPreloads, libraryTags, defaultTheme, scriptPaths) {
1415
- const escapedMeta = escapeTemplateLiteral3(result.meta);
1416
- const escapedHTML = escapeTemplateLiteral3(result.html);
1417
- const escapedFontPreloads = escapeTemplateLiteral3(fontPreloads);
1418
- const scriptsArrayLiteral = scriptPaths.length > 0 ? `[${scriptPaths.map((s) => `"${s}"`).join(", ")}]` : "[]";
1419
- const libraryTagsLiteral = `{ headCSS: \`${escapeTemplateLiteral3(libraryTags.headCSS || "")}\`, headJS: \`${escapeTemplateLiteral3(libraryTags.headJS || "")}\`, bodyEndJS: \`${escapeTemplateLiteral3(libraryTags.bodyEndJS || "")}\` }`;
1420
- return `---
1421
- import BaseLayout from '${importPath}';
1422
- ---
1423
- <BaseLayout
1424
- title="${result.title.replace(/"/g, "&quot;")}"
1425
- meta={\`${escapedMeta}\`}
1426
- scripts={${scriptsArrayLiteral}}
1427
- locale="${result.locale}"
1428
- theme="${defaultTheme}"
1429
- fontPreloads={\`${escapedFontPreloads}\`}
1430
- libraryTags={${libraryTagsLiteral}}
1431
- >
1432
- <Fragment set:html={\`<div id="root">${escapedHTML}</div>\`} />
1433
- </BaseLayout>
1434
- `;
1435
- }
1436
- async function buildAstroProject(projectRoot, outputDir) {
1437
- const startTime = Date.now();
1438
- console.log("\u{1F3D7}\uFE0F Building Astro export...\n");
644
+ async function buildWebflowPayload(projectRoot) {
1439
645
  configService.reset();
1440
646
  const projectConfig = await loadProjectConfig();
1441
647
  const siteUrl = projectConfig.siteUrl?.replace(/\/$/, "") || "";
1442
648
  const i18nConfig = await loadI18nConfig();
1443
- console.log(`\u{1F310} Locales: ${i18nConfig.locales.map((l) => l.code).join(", ")} (default: ${i18nConfig.defaultLocale})
1444
- `);
1445
649
  await migrateTemplatesDirectory();
1446
- const { components, warnings, errors: compErrors } = await loadComponentDirectory(projectPaths.components());
650
+ const { components } = await loadComponentDirectory(projectPaths.components());
1447
651
  const globalComponents = {};
1448
652
  components.forEach((value, key) => {
1449
653
  globalComponents[key] = value;
1450
654
  });
1451
- for (const w of warnings) console.warn(` Warning: ${w}`);
1452
- for (const e of compErrors) console.error(` Error: ${e}`);
1453
- console.log(`Loaded ${components.size} global component(s)
1454
- `);
1455
655
  const cmsProvider = new FileSystemCMSProvider(projectPaths.templates(), projectPaths.cms());
1456
656
  const cmsService = new CMSService(cmsProvider);
1457
657
  await cmsService.initialize();
1458
- console.log("CMS service initialized\n");
1459
658
  const themeConfig = await colorService.loadThemeConfig();
1460
659
  const variablesConfig = await variableService.loadConfig();
1461
660
  const breakpoints = await loadBreakpointConfig();
1462
661
  const responsiveScales = await loadResponsiveScalesConfig();
1463
662
  await configService.load();
1464
- const globalLibraries = configService.getLibraries();
1465
- const componentLibraries = collectComponentLibraries(globalComponents);
1466
- const outDir = outputDir || join(projectPaths.project, "astro-export");
1467
- if (existsSync(outDir)) {
1468
- rmSync(outDir, { recursive: true, force: true });
1469
- }
1470
- mkdirSync(outDir, { recursive: true });
1471
- const srcDir = join(outDir, "src");
1472
- const pagesOutDir = join(srcDir, "pages");
1473
- const layoutsDir = join(srcDir, "layouts");
1474
- const stylesDir = join(srcDir, "styles");
1475
- const componentsOutDir = join(srcDir, "components");
1476
- const publicDir = join(outDir, "public");
1477
- const scriptsDir = join(publicDir, "_scripts");
1478
- for (const d of [srcDir, pagesOutDir, layoutsDir, stylesDir, componentsOutDir, publicDir]) {
1479
- mkdirSync(d, { recursive: true });
1480
- }
1481
663
  const pagesDir = projectPaths.pages();
1482
664
  if (!existsSync(pagesDir)) {
1483
- console.error("Pages directory not found!");
1484
- return { pages: 0, cmsPages: 0, collections: 0, errors: 1 };
665
+ return emptyPayload();
1485
666
  }
1486
667
  const pageFiles = scanJSONFiles(pagesDir);
1487
668
  if (pageFiles.length === 0) {
1488
- console.warn("No pages found in ./pages directory");
1489
- return { pages: 0, cmsPages: 0, collections: 0, errors: 0 };
669
+ return emptyPayload();
1490
670
  }
1491
- console.log(`Found ${pageFiles.length} page(s) to process
1492
- `);
1493
671
  const slugMappings = [];
1494
672
  for (const file of pageFiles) {
1495
673
  const pageName = file.replace(".json", "");
@@ -1505,61 +683,16 @@ async function buildAstroProject(projectRoot, outputDir) {
1505
683
  } catch {
1506
684
  }
1507
685
  }
1508
- const allResults = [];
1509
- const allInteractiveStyles = /* @__PURE__ */ new Map();
1510
- const allComponentCSS = /* @__PURE__ */ new Set();
1511
- const jsContents = /* @__PURE__ */ new Map();
1512
- let errorCount = 0;
1513
- function mergeInteractiveStyles(source) {
1514
- for (const [key, value] of source) {
1515
- if (!allInteractiveStyles.has(key)) {
1516
- allInteractiveStyles.set(key, value);
1517
- }
1518
- }
1519
- }
1520
- function processRenderResult(result, urlPath, astroFilePath, fileDepth, pageData, pageName, isCMSPage2) {
1521
- mergeInteractiveStyles(result.interactiveStylesMap);
1522
- if (result.componentCSS) {
1523
- allComponentCSS.add(result.componentCSS);
1524
- }
1525
- if (result.javascript) {
1526
- const hash = hashContent2(result.javascript);
1527
- if (!jsContents.has(hash)) {
1528
- jsContents.set(hash, result.javascript);
1529
- }
1530
- }
1531
- allResults.push({
1532
- html: result.html,
1533
- meta: result.meta,
1534
- title: result.title,
1535
- javascript: result.javascript,
1536
- componentCSS: result.componentCSS,
1537
- locale: result.locale,
1538
- interactiveStylesMap: result.interactiveStylesMap,
1539
- urlPath,
1540
- fileDepth,
1541
- astroFilePath,
1542
- pageData,
1543
- pageName,
1544
- isCMSPage: isCMSPage2
1545
- });
1546
- }
686
+ const allPages = [];
687
+ const allStyleClasses = /* @__PURE__ */ new Map();
1547
688
  for (const file of pageFiles) {
1548
689
  const pageName = file.replace(".json", "");
1549
690
  const basePath = mapPageNameToPath(pageName);
1550
691
  const pageContent = await loadJSONFile(join(pagesDir, file));
1551
- if (!pageContent) {
1552
- console.warn(` Skipping ${basePath} (empty file)`);
1553
- errorCount++;
1554
- continue;
1555
- }
692
+ if (!pageContent) continue;
1556
693
  try {
1557
694
  const pageData = parseJSON(pageContent);
1558
- const isDevBuild = process.env.MENO_DEV_BUILD === "true";
1559
- if (pageData.meta?.draft === true && !isDevBuild) {
1560
- console.log(` Skipping draft: ${basePath}`);
1561
- continue;
1562
- }
695
+ if (pageData.meta?.draft === true) continue;
1563
696
  const slugs = pageData.meta?.slugs;
1564
697
  for (const localeConfig of i18nConfig.locales) {
1565
698
  const locale = localeConfig.code;
@@ -1573,9 +706,6 @@ async function buildAstroProject(projectRoot, outputDir) {
1573
706
  slug = basePath.substring(1);
1574
707
  }
1575
708
  const urlPath = isDefault ? slug === "" ? "/" : `/${slug}` : slug === "" ? `/${locale}` : `/${locale}/${slug}`;
1576
- const astroFileName = slug === "" ? "index.astro" : `${slug}.astro`;
1577
- const astroFilePath = isDefault ? astroFileName : `${locale}/${astroFileName}`;
1578
- const fileDepth = astroFilePath.split("/").length - 1;
1579
709
  const result = await renderPageSSR(
1580
710
  pageData,
1581
711
  globalComponents,
@@ -1585,60 +715,79 @@ async function buildAstroProject(projectRoot, outputDir) {
1585
715
  i18nConfig,
1586
716
  slugMappings,
1587
717
  void 0,
1588
- // cmsContext
1589
718
  cmsService,
1590
719
  true
1591
- // isProductionBuild
1592
720
  );
1593
- processRenderResult(result, urlPath, astroFilePath, fileDepth, pageData, pageName, false);
1594
- console.log(` Rendered: ${urlPath}`);
721
+ const ctx = {
722
+ globalComponents,
723
+ elementPath: [0],
724
+ fileType: "page",
725
+ fileName: pageName,
726
+ breakpoints,
727
+ styleClasses: allStyleClasses
728
+ };
729
+ const body = pageData.root || pageData.node;
730
+ const elements = body ? nodeToWebflow(body, ctx) : [];
731
+ allPages.push({
732
+ title: result.title,
733
+ slug: slug || "index",
734
+ metaDescription: typeof pageData.meta?.description === "string" ? pageData.meta.description : void 0,
735
+ elements,
736
+ locale
737
+ });
1595
738
  }
1596
739
  } catch (error) {
1597
- console.error(` Error rendering ${basePath}:`, error?.message || error);
1598
- errorCount++;
740
+ console.error(`Error processing ${basePath}:`, error?.message);
1599
741
  }
1600
742
  }
1601
743
  const templatesDir = projectPaths.templates();
1602
- const templateSchemas = [];
1603
- let cmsPageCount = 0;
744
+ const cmsCollections = [];
1604
745
  if (existsSync(templatesDir)) {
1605
746
  const templateFiles = readdirSync(templatesDir).filter((f) => f.endsWith(".json"));
1606
- if (templateFiles.length > 0) {
1607
- console.log(`
1608
- Processing ${templateFiles.length} CMS template(s)...
1609
- `);
1610
- }
1611
747
  for (const file of templateFiles) {
1612
748
  const templateContent = await loadJSONFile(join(templatesDir, file));
1613
749
  if (!templateContent) continue;
1614
750
  try {
1615
751
  const pageData = parseJSON(templateContent);
1616
- const isDevBuild = process.env.MENO_DEV_BUILD === "true";
1617
- if (pageData.meta?.draft === true && !isDevBuild) {
1618
- console.log(` Skipping draft template: ${file}`);
1619
- continue;
1620
- }
1621
- if (!isCMSPage(pageData)) {
1622
- console.warn(` ${file} is in templates/ but missing meta.source: "cms"`);
1623
- continue;
1624
- }
752
+ if (pageData.meta?.draft === true) continue;
753
+ if (!isCMSPage(pageData)) continue;
1625
754
  const cmsSchema = pageData.meta.cms;
1626
- templateSchemas.push(cmsSchema);
1627
- console.log(` CMS Collection: ${cmsSchema.id}`);
1628
755
  const items = await cmsService.queryItems({ collection: cmsSchema.id });
1629
- if (items.length === 0) {
1630
- console.log(` No items found in cms/${cmsSchema.id}/`);
1631
- continue;
756
+ const fields = [];
757
+ if (cmsSchema.fields) {
758
+ for (const [fieldName, fieldDef] of Object.entries(cmsSchema.fields)) {
759
+ fields.push({
760
+ name: fieldDef.label || fieldName,
761
+ slug: fieldName,
762
+ type: mapCMSFieldType(fieldDef.type),
763
+ required: fieldDef.required,
764
+ options: fieldDef.options
765
+ });
766
+ }
767
+ }
768
+ const resolvedItems = [];
769
+ for (const item of items) {
770
+ const resolved = {};
771
+ for (const [key, value] of Object.entries(item)) {
772
+ if (isI18nValue(value)) {
773
+ resolved[key] = resolveI18nValue(value, i18nConfig.defaultLocale, i18nConfig);
774
+ } else {
775
+ resolved[key] = value;
776
+ }
777
+ }
778
+ resolvedItems.push(resolved);
1632
779
  }
1633
- console.log(` Found ${items.length} item(s)`);
780
+ cmsCollections.push({
781
+ name: cmsSchema.id,
782
+ slug: cmsSchema.id,
783
+ urlPattern: cmsSchema.urlPattern,
784
+ fields,
785
+ items: resolvedItems
786
+ });
1634
787
  for (const item of items) {
1635
788
  for (const localeConfig of i18nConfig.locales) {
1636
789
  const locale = localeConfig.code;
1637
- const isDefault = locale === i18nConfig.defaultLocale;
1638
- const isDevBuild2 = process.env.MENO_DEV_BUILD === "true";
1639
- if (!isDevBuild2 && isItemDraftForLocale(item, locale)) {
1640
- continue;
1641
- }
790
+ if (isItemDraftForLocale(item, locale)) continue;
1642
791
  const itemPath = buildCMSItemPath(cmsSchema.urlPattern, item, cmsSchema.slugField, locale, i18nConfig);
1643
792
  const itemWithUrl = { ...item, _url: itemPath };
1644
793
  const result = await renderPageSSR(
@@ -1653,293 +802,53 @@ Processing ${templateFiles.length} CMS template(s)...
1653
802
  cmsService,
1654
803
  true
1655
804
  );
1656
- const pathWithoutSlash = itemPath.startsWith("/") ? itemPath.substring(1) : itemPath;
1657
- const astroFilePath = isDefault ? `${pathWithoutSlash}.astro` : `${locale}/${pathWithoutSlash}.astro`;
1658
- const fileDepth = astroFilePath.split("/").length - 1;
1659
- const urlPath = isDefault ? itemPath : `/${locale}${itemPath}`;
1660
- processRenderResult(result, urlPath, astroFilePath, fileDepth, pageData, file.replace(".json", ""), true);
1661
- console.log(` Rendered: ${urlPath}`);
1662
- cmsPageCount++;
805
+ const ctx = {
806
+ globalComponents,
807
+ elementPath: [0],
808
+ fileType: "page",
809
+ fileName: file.replace(".json", ""),
810
+ breakpoints,
811
+ styleClasses: allStyleClasses
812
+ };
813
+ const body = pageData.root || pageData.node;
814
+ const cmsProps = { cms: itemWithUrl };
815
+ const elements = body ? nodeToWebflow(body, ctx, cmsProps) : [];
816
+ const slug = itemPath.startsWith("/") ? itemPath.substring(1) : itemPath;
817
+ allPages.push({
818
+ title: result.title,
819
+ slug,
820
+ elements,
821
+ locale
822
+ });
1663
823
  }
1664
824
  }
1665
825
  } catch (error) {
1666
- console.error(` Error processing template ${file}:`, error?.message || error);
1667
- errorCount++;
826
+ console.error(`Error processing template ${file}:`, error?.message);
1668
827
  }
1669
828
  }
1670
829
  }
1671
- const mappingClasses = collectAllMappingClasses(globalComponents, breakpoints);
1672
- const fontCSS = generateFontCSS();
1673
830
  const themeColorCSS = generateThemeColorVariablesCSS(themeConfig);
1674
831
  const variablesCSS = generateVariablesCSS(variablesConfig, breakpoints, responsiveScales);
1675
- const interactiveCSS = generateAllInteractiveCSS(allInteractiveStyles, breakpoints);
1676
- const componentCSSCombined = Array.from(allComponentCSS).join("\n");
1677
- const baseCSS = `@layer base {
1678
- * { margin: 0; padding: 0; box-sizing: border-box; }
1679
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; }
1680
- button { background: none; border: none; padding: 0; font: inherit; cursor: pointer; outline: inherit; }
1681
- img { display: block; width: 100%; height: 100%; }
1682
- picture { display: block; }
1683
- .olink { text-decoration: none; display: block; }
1684
- .oem { display: inline-block; }
1685
- }`;
1686
- const tailwindDirectives = `@tailwind base;
1687
- @tailwind components;
1688
- @tailwind utilities;`;
1689
- const globalCSS = [tailwindDirectives, fontCSS, themeColorCSS, variablesCSS, baseCSS, componentCSSCombined, interactiveCSS].filter(Boolean).join("\n\n");
1690
- await writeFile2(join(stylesDir, "global.css"), globalCSS, "utf-8");
1691
- console.log(`
1692
- Generated global.css (${(globalCSS.length / 1024).toFixed(1)} KB)`);
1693
- const fontPreloads = generateFontPreloadTags();
1694
- const mergedLibraries = mergeLibraries(globalLibraries, componentLibraries);
1695
- const buildLibraries = filterLibrariesByContext(mergedLibraries, "build");
1696
- const libraryTags = generateLibraryTags(buildLibraries);
1697
- const baseLayoutContent = `---
1698
- import '../styles/global.css';
1699
-
1700
- interface Props {
1701
- title: string;
1702
- meta?: string;
1703
- scripts?: string[];
1704
- locale?: string;
1705
- theme?: string;
1706
- fontPreloads?: string;
1707
- libraryTags?: { headCSS?: string; headJS?: string; bodyEndJS?: string };
1708
- }
1709
-
1710
- const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.default || "light"}', fontPreloads = '', libraryTags = {} } = Astro.props;
1711
- ---
1712
- <!DOCTYPE html>
1713
- <html lang={locale} data-theme={theme}>
1714
- <head>
1715
- <meta charset="UTF-8">
1716
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1717
- <Fragment set:html={fontPreloads} />
1718
- <Fragment set:html={libraryTags.headCSS || ''} />
1719
- <Fragment set:html={libraryTags.headJS || ''} />
1720
- <Fragment set:html={meta} />
1721
- <title>{title}</title>
1722
- </head>
1723
- <body>
1724
- <slot />
1725
- {scripts.map((s) => <script src={s} />)}
1726
- <Fragment set:html={libraryTags.bodyEndJS || ''} />
1727
- </body>
1728
- </html>
1729
- `;
1730
- await writeFile2(join(layoutsDir, "BaseLayout.astro"), baseLayoutContent, "utf-8");
1731
- console.log("Generated BaseLayout.astro");
1732
- let componentFileCount = 0;
1733
- for (const [compName, compDef] of Object.entries(globalComponents)) {
1734
- try {
1735
- const astroContent = emitAstroComponent(compName, compDef, globalComponents, breakpoints);
1736
- await writeFile2(join(componentsOutDir, `${compName}.astro`), astroContent, "utf-8");
1737
- componentFileCount++;
1738
- } catch (error) {
1739
- console.warn(` Warning: could not generate component ${compName}: ${error?.message}`);
1740
- }
1741
- }
1742
- console.log(`Generated ${componentFileCount} component .astro file(s)`);
1743
- const defaultTheme = themeConfig.default || "light";
1744
- for (const result of allResults) {
1745
- const importPath = layoutImportPath(result.fileDepth);
1746
- const scriptPaths = [];
1747
- if (result.javascript) {
1748
- const hash = hashContent2(result.javascript);
1749
- const scriptFile = `${hash}.js`;
1750
- const scriptPublicPath = `/_scripts/${scriptFile}`;
1751
- if (!existsSync(scriptsDir)) {
1752
- mkdirSync(scriptsDir, { recursive: true });
1753
- }
1754
- const fullScriptPath = join(scriptsDir, scriptFile);
1755
- if (!existsSync(fullScriptPath)) {
1756
- await writeFile2(fullScriptPath, result.javascript, "utf-8");
1757
- }
1758
- scriptPaths.push(scriptPublicPath);
1759
- }
1760
- let astroContent;
1761
- if (result.pageData && !result.isCMSPage) {
1762
- try {
1763
- astroContent = emitAstroPage({
1764
- pageData: result.pageData,
1765
- globalComponents,
1766
- title: result.title,
1767
- meta: result.meta,
1768
- locale: result.locale,
1769
- theme: defaultTheme,
1770
- fontPreloads,
1771
- libraryTags,
1772
- scriptPaths,
1773
- layoutImportPath: importPath,
1774
- fileDepth: result.fileDepth,
1775
- ssrFallbacks: /* @__PURE__ */ new Map(),
1776
- // SSR fallbacks for complex nodes
1777
- pageName: result.pageName || "index",
1778
- breakpoints
1779
- });
1780
- } catch (error) {
1781
- console.warn(` Warning: component emission failed for ${result.urlPath}, using SSR fallback: ${error?.message}`);
1782
- astroContent = buildSSRFallbackPage(result, importPath, fontPreloads, libraryTags, defaultTheme, scriptPaths);
1783
- }
1784
- } else {
1785
- astroContent = buildSSRFallbackPage(result, importPath, fontPreloads, libraryTags, defaultTheme, scriptPaths);
1786
- }
1787
- const astroFileFull = join(pagesOutDir, result.astroFilePath);
1788
- const astroFileDir = astroFileFull.substring(0, astroFileFull.lastIndexOf("/"));
1789
- if (!existsSync(astroFileDir)) {
1790
- mkdirSync(astroFileDir, { recursive: true });
1791
- }
1792
- await writeFile2(astroFileFull, astroContent, "utf-8");
1793
- }
1794
- console.log(`Generated ${allResults.length} .astro page file(s)`);
1795
- let collectionCount = 0;
1796
- if (templateSchemas.length > 0) {
1797
- const contentDir = join(srcDir, "content");
1798
- mkdirSync(contentDir, { recursive: true });
1799
- const collectionDefs = [];
1800
- for (const schema of templateSchemas) {
1801
- const collectionDir = join(contentDir, schema.id);
1802
- mkdirSync(collectionDir, { recursive: true });
1803
- const cmsItemsDir = join(projectPaths.cms(), schema.id);
1804
- if (existsSync(cmsItemsDir)) {
1805
- const itemFiles = readdirSync(cmsItemsDir).filter((f) => f.endsWith(".json"));
1806
- for (const itemFile of itemFiles) {
1807
- try {
1808
- const rawContent = await readFile(join(cmsItemsDir, itemFile), "utf-8");
1809
- const item = JSON.parse(rawContent);
1810
- const resolved = {};
1811
- for (const [key, value] of Object.entries(item)) {
1812
- if (isI18nValue(value)) {
1813
- resolved[key] = resolveI18nValue(value, i18nConfig.defaultLocale, i18nConfig);
1814
- } else {
1815
- resolved[key] = value;
1816
- }
1817
- }
1818
- await writeFile2(
1819
- join(collectionDir, itemFile),
1820
- JSON.stringify(resolved, null, 2),
1821
- "utf-8"
1822
- );
1823
- } catch (err) {
1824
- console.warn(` Warning: could not process CMS item ${itemFile}: ${err?.message}`);
1825
- }
1826
- }
1827
- }
1828
- const fieldDefs = [];
1829
- if (schema.fields) {
1830
- for (const [fieldName, fieldDef] of Object.entries(schema.fields)) {
1831
- const zodType = cmsFieldToZod(fieldDef);
1832
- const optional = fieldDef.required ? "" : ".optional()";
1833
- fieldDefs.push(` ${fieldName}: ${zodType}${optional}`);
1834
- }
1835
- }
1836
- collectionDefs.push(` '${schema.id}': defineCollection({
1837
- type: 'data',
1838
- schema: z.object({
1839
- ${fieldDefs.join(",\n")}
1840
- })
1841
- })`);
1842
- collectionCount++;
1843
- }
1844
- const configContent = `import { z, defineCollection } from 'astro:content';
1845
-
1846
- const collections = {
1847
- ${collectionDefs.join(",\n")}
1848
- };
1849
-
1850
- export { collections };
1851
- `;
1852
- await writeFile2(join(contentDir, "config.ts"), configContent, "utf-8");
1853
- console.log(`Generated ${collectionCount} content collection(s) with config.ts`);
1854
- }
1855
- const assetDirs = ["fonts", "images", "icons", "videos", "assets"];
1856
- let copiedAssets = 0;
1857
- for (const dir of assetDirs) {
1858
- const srcAssetDir = join(projectPaths.project, dir);
1859
- if (existsSync(srcAssetDir)) {
1860
- copyDirectory(srcAssetDir, join(publicDir, dir));
1861
- copiedAssets++;
1862
- }
1863
- }
1864
- const librariesDir = join(projectPaths.project, "libraries");
1865
- if (existsSync(librariesDir)) {
1866
- copyDirectory(librariesDir, join(publicDir, "libraries"));
1867
- copiedAssets++;
1868
- }
1869
- if (copiedAssets > 0) {
1870
- console.log(`Copied ${copiedAssets} asset director${copiedAssets === 1 ? "y" : "ies"} to public/`);
1871
- }
1872
- const packageJson = {
1873
- name: "astro-export",
1874
- type: "module",
1875
- version: "0.0.1",
1876
- private: true,
1877
- scripts: {
1878
- dev: "astro dev",
1879
- start: "astro dev",
1880
- build: "astro build",
1881
- preview: "astro preview"
1882
- },
1883
- dependencies: {
1884
- "astro": "^4.0.0",
1885
- "@astrojs/sitemap": "^3.0.0",
1886
- "@astrojs/tailwind": "^5.0.0",
1887
- "tailwindcss": "^3.4.0"
1888
- }
1889
- };
1890
- await writeFile2(join(outDir, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8");
1891
- const astroConfig = `import { defineConfig } from 'astro/config';
1892
- import sitemap from '@astrojs/sitemap';
1893
- import tailwind from '@astrojs/tailwind';
1894
-
1895
- export default defineConfig({${siteUrl ? `
1896
- site: '${siteUrl}',` : ""}
1897
- integrations: [sitemap(), tailwind({ applyBaseStyles: false })],
1898
- });
1899
- `;
1900
- const safelistArray = Array.from(mappingClasses);
1901
- const safelistLiteral = safelistArray.length > 0 ? `
1902
- safelist: [
1903
- ${safelistArray.map((c) => ` '${c}'`).join(",\n")}
1904
- ],` : "";
1905
- const tailwindConfig = `/** @type {import('tailwindcss').Config} */
1906
- export default {
1907
- content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],${safelistLiteral}
1908
- theme: {
1909
- extend: {},
1910
- },
1911
- plugins: [],
1912
- };
1913
- `;
1914
- await writeFile2(join(outDir, "tailwind.config.mjs"), tailwindConfig, "utf-8");
1915
- await writeFile2(join(outDir, "astro.config.mjs"), astroConfig, "utf-8");
1916
- const tsConfig = {
1917
- extends: "astro/tsconfigs/strict"
832
+ const cssVariables = extractCSSVariables(themeColorCSS, variablesCSS);
833
+ const assets = scanAssets(projectPaths.project);
834
+ return {
835
+ version: 1,
836
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
837
+ pages: allPages,
838
+ styles: Array.from(allStyleClasses.values()),
839
+ cms: cmsCollections,
840
+ assets,
841
+ cssVariables: Object.keys(cssVariables).length > 0 ? cssVariables : void 0
1918
842
  };
1919
- await writeFile2(join(outDir, "tsconfig.json"), JSON.stringify(tsConfig, null, 2), "utf-8");
1920
- console.log("Generated package.json, astro.config.mjs, tailwind.config.mjs, tsconfig.json");
1921
- const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
1922
- const totalPages = allResults.length;
1923
- console.log("\n" + "=".repeat(50));
1924
- console.log("Astro export complete!");
1925
- console.log(` Pages: ${totalPages - cmsPageCount}`);
1926
- if (cmsPageCount > 0) {
1927
- console.log(` CMS pages: ${cmsPageCount}`);
1928
- }
1929
- if (collectionCount > 0) {
1930
- console.log(` Content collections: ${collectionCount}`);
1931
- }
1932
- if (errorCount > 0) {
1933
- console.log(` Errors: ${errorCount}`);
1934
- }
1935
- console.log(` Time: ${elapsed}s`);
1936
- console.log(` Output: ${outDir}`);
1937
- console.log("");
843
+ }
844
+ function emptyPayload() {
1938
845
  return {
1939
- pages: totalPages - cmsPageCount,
1940
- cmsPages: cmsPageCount,
1941
- collections: collectionCount,
1942
- errors: errorCount
846
+ version: 1,
847
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
848
+ pages: [],
849
+ styles: [],
850
+ cms: [],
851
+ assets: []
1943
852
  };
1944
853
  }
1945
854
 
@@ -1964,12 +873,12 @@ export {
1964
873
  SERVE_PORT,
1965
874
  VariableService,
1966
875
  WebSocketManager,
1967
- buildAstroProject,
1968
876
  buildAttributes,
1969
877
  buildComponentHTML,
1970
878
  buildImageMetadataMap,
1971
879
  buildLineMap,
1972
880
  buildStaticPages,
881
+ buildWebflowPayload,
1973
882
  bundleFile,
1974
883
  clearJSValidationCache,
1975
884
  collectComponentCSS,
@@ -1988,8 +897,12 @@ export {
1988
897
  extractPageMeta,
1989
898
  fileExists,
1990
899
  generateBuildErrorPage,
900
+ generateFontCSS,
901
+ generateFontPreloadTags,
1991
902
  generateMetaTags,
1992
903
  generateSSRHTML,
904
+ generateThemeColorVariablesCSS,
905
+ generateVariablesCSS,
1993
906
  getFileSize,
1994
907
  getJSValidationErrors,
1995
908
  getMimeType,
@@ -2008,13 +921,20 @@ export {
2008
921
  isBun,
2009
922
  jsonResponse,
2010
923
  lineMapToObject,
924
+ loadBreakpointConfig,
925
+ loadComponentDirectory,
926
+ loadI18nConfig,
927
+ loadJSONFile,
2011
928
  loadProjectConfig,
929
+ loadResponsiveScalesConfig,
2012
930
  logError,
2013
931
  logRequest,
2014
932
  logResponseTime,
933
+ mapPageNameToPath,
2015
934
  migrateTemplatesDirectory,
2016
935
  minifyJS,
2017
936
  packagePaths,
937
+ parseJSON,
2018
938
  processCMSPropsTemplate,
2019
939
  processCMSTemplate,
2020
940
  projectPaths,