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
@@ -76,6 +76,47 @@ function formatDefault(def: PropDefinition): string | null {
76
76
  return JSON.stringify(val);
77
77
  }
78
78
 
79
+ /**
80
+ * Merge the `className` variable onto the root element's class attribute.
81
+ * Handles: class="existing", class:list={[...]}, class={expr}, or no class at all.
82
+ */
83
+ function mergeClassNameOntoRoot(template: string): string {
84
+ // Find the first opening tag (skip whitespace/newlines)
85
+ const tagMatch = template.match(/^(\s*<)(\w[\w-]*)([\s\S]*?)(\/?>)/);
86
+ if (!tagMatch) return template;
87
+
88
+ const [fullMatch, prefix, tagName, attrs, close] = tagMatch;
89
+
90
+ // Check for existing class:list
91
+ const classListMatch = attrs.match(/\s+class:list=\{(\[[\s\S]*?\])\}/);
92
+ if (classListMatch) {
93
+ const existingList = classListMatch[1];
94
+ // Append className to the array
95
+ const newList = existingList.replace(/\]$/, `, className]`);
96
+ const newAttrs = attrs.replace(classListMatch[0], ` class:list={${newList}}`);
97
+ return prefix + tagName + newAttrs + close + template.slice(fullMatch.length);
98
+ }
99
+
100
+ // Check for existing class="..." (static)
101
+ const classStaticMatch = attrs.match(/\s+class="([^"]*)"/);
102
+ if (classStaticMatch) {
103
+ const existing = classStaticMatch[1];
104
+ const newAttrs = attrs.replace(classStaticMatch[0], ` class:list={["${existing}", className]}`);
105
+ return prefix + tagName + newAttrs + close + template.slice(fullMatch.length);
106
+ }
107
+
108
+ // Check for existing class={expr} (dynamic)
109
+ const classDynMatch = attrs.match(/\s+class=\{([^}]+)\}/);
110
+ if (classDynMatch) {
111
+ const expr = classDynMatch[1];
112
+ const newAttrs = attrs.replace(classDynMatch[0], ` class:list={[${expr}, className]}`);
113
+ return prefix + tagName + newAttrs + close + template.slice(fullMatch.length);
114
+ }
115
+
116
+ // No existing class — add class={className}
117
+ return prefix + tagName + ` class={className}` + attrs + close + template.slice(fullMatch.length);
118
+ }
119
+
79
120
  // ---------------------------------------------------------------------------
80
121
  // Main emitter
81
122
  // ---------------------------------------------------------------------------
@@ -87,7 +128,8 @@ export function emitAstroComponent(
87
128
  name: string,
88
129
  def: ComponentDefinition,
89
130
  allComponents: Record<string, ComponentDefinition>,
90
- breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS
131
+ breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
132
+ defaultLocale: string = 'en'
91
133
  ): string {
92
134
  const comp = def.component;
93
135
  const propDefs = comp.interface || {};
@@ -110,17 +152,23 @@ export function emitAstroComponent(
110
152
  fileType: 'component',
111
153
  fileName: name,
112
154
  breakpoints,
155
+ defaultLocale,
113
156
  };
114
157
 
115
158
  // Emit the template body
116
- const templateBody = nodeToAstro(structure, ctx);
159
+ let templateBody = nodeToAstro(structure, ctx);
160
+
161
+ // Merge instance className onto the root element (for acceptsStyles support)
162
+ templateBody = mergeClassNameOntoRoot(templateBody);
117
163
 
118
- // Build frontmatter
119
- const frontmatter = buildFrontmatter(name, propDefs, ctx.imports, ctx.dynamicTags);
164
+ // Build frontmatter (includes class prop for instance style support)
165
+ const frontmatter = buildFrontmatter(name, propDefs, ctx.imports, ctx.dynamicTags, ctx.needsI18nResolver ? defaultLocale : undefined);
120
166
 
121
167
  // Build style/script sections
122
168
  const styleSection = comp.css ? `\n<style>\n${comp.css}\n</style>\n` : '';
123
- const scriptSection = comp.javascript ? `\n<script>\n${comp.javascript}\n</script>\n` : '';
169
+ const scriptSection = comp.javascript
170
+ ? buildScriptSection(comp.javascript, comp, propDefs)
171
+ : '';
124
172
 
125
173
  return `---\n${frontmatter}---\n${templateBody}${styleSection}${scriptSection}`;
126
174
  }
@@ -132,7 +180,8 @@ function buildFrontmatter(
132
180
  componentName: string,
133
181
  propDefs: Record<string, PropDefinition>,
134
182
  imports: Set<string>,
135
- dynamicTags?: Map<string, string>
183
+ dynamicTags?: Map<string, string>,
184
+ i18nDefaultLocale?: string
136
185
  ): string {
137
186
  const lines: string[] = [];
138
187
 
@@ -145,7 +194,8 @@ function buildFrontmatter(
145
194
 
146
195
  const propEntries = Object.entries(propDefs);
147
196
 
148
- if (propEntries.length > 0) {
197
+ // Always generate Props interface and destructuring (at minimum for class prop)
198
+ {
149
199
  // Interface
150
200
  lines.push('interface Props {');
151
201
  for (const [propName, propDef] of propEntries) {
@@ -154,6 +204,8 @@ function buildFrontmatter(
154
204
  const optional = 'default' in propDef && propDef.default !== undefined;
155
205
  lines.push(` ${propName}${optional ? '?' : ''}: ${tsType};`);
156
206
  }
207
+ // Always include class prop for instance style support
208
+ lines.push(' class?: string;');
157
209
  lines.push('}');
158
210
  lines.push('');
159
211
 
@@ -164,21 +216,24 @@ function buildFrontmatter(
164
216
  const defaultVal = formatDefault(propDef);
165
217
  if (defaultVal !== null) {
166
218
  destructParts.push(`${propName} = ${defaultVal}`);
219
+ } else if (propDef.type === 'link') {
220
+ destructParts.push(`${propName} = { href: "#" }`);
167
221
  } else {
168
222
  destructParts.push(propName);
169
223
  }
170
224
  }
171
225
 
172
- if (destructParts.length > 0) {
173
- if (destructParts.length <= 3 && destructParts.join(', ').length < 80) {
174
- lines.push(`const { ${destructParts.join(', ')} } = Astro.props;`);
175
- } else {
176
- lines.push('const {');
177
- for (const part of destructParts) {
178
- lines.push(` ${part},`);
179
- }
180
- lines.push('} = Astro.props;');
226
+ // Always include class prop (renamed to className to avoid reserved word)
227
+ destructParts.push('class: className = ""');
228
+
229
+ if (destructParts.length <= 3 && destructParts.join(', ').length < 80) {
230
+ lines.push(`const { ${destructParts.join(', ')} } = Astro.props;`);
231
+ } else {
232
+ lines.push('const {');
233
+ for (const part of destructParts) {
234
+ lines.push(` ${part},`);
181
235
  }
236
+ lines.push('} = Astro.props;');
182
237
  }
183
238
  }
184
239
 
@@ -190,6 +245,18 @@ function buildFrontmatter(
190
245
  }
191
246
  }
192
247
 
248
+ // i18n resolver helper — resolves { _i18n: true, en: "...", pl: "..." } at runtime
249
+ if (i18nDefaultLocale) {
250
+ lines.push('');
251
+ lines.push(`const r = (v: any) => {`);
252
+ lines.push(` if (v && typeof v === 'object' && v._i18n) {`);
253
+ lines.push(` const locale = Astro.currentLocale ?? '${i18nDefaultLocale}';`);
254
+ lines.push(` return v[locale] ?? v['${i18nDefaultLocale}'] ?? Object.values(v).find((s: any) => typeof s === 'string' && s !== '') ?? '';`);
255
+ lines.push(` }`);
256
+ lines.push(` return v ?? '';`);
257
+ lines.push(`};`);
258
+ }
259
+
193
260
  if (lines.length > 0) lines.push('');
194
261
  return lines.join('\n');
195
262
  }
@@ -203,6 +270,72 @@ function buildNoStructureComponent(
203
270
  ): string {
204
271
  let content = '---\n---\n<slot />\n';
205
272
  if (comp.css) content += `\n<style>\n${comp.css}\n</style>\n`;
206
- if (comp.javascript) content += `\n<script>\n${comp.javascript}\n</script>\n`;
273
+ if (comp.javascript) content += `\n<script is:inline>\n${comp.javascript}\n</script>\n`;
207
274
  return content;
208
275
  }
276
+
277
+ /**
278
+ * Transform JS for define:vars compatibility.
279
+ * Astro's define:vars injects each prop as a script-scope variable, not a `props` object.
280
+ * This function:
281
+ * 1. Removes `const/let/var { x, y } = props;` destructuring lines
282
+ * 2. Replaces `props.X` references with direct `X` variable access
283
+ * 3. Drops `var/let/const` from redeclarations of define:vars variables
284
+ */
285
+ function transformDefineVarsJS(js: string, varNames: string[]): string {
286
+ let result = js;
287
+
288
+ // 1. Remove destructuring from props: `const { x, y } = props;`
289
+ result = result.replace(
290
+ /^\s*(const|let|var)\s+\{([^}]+)\}\s*=\s*props\s*;?\s*$/gm,
291
+ (match, _keyword, inner) => {
292
+ const names = inner.split(',').map((s: string) => s.trim()).filter(Boolean);
293
+ if (names.every((n: string) => varNames.includes(n))) return '';
294
+ return match;
295
+ }
296
+ );
297
+
298
+ // 2. Replace `props.X` with `X` (longest names first to avoid substring conflicts)
299
+ const sorted = [...varNames].sort((a, b) => b.length - a.length);
300
+ for (const name of sorted) {
301
+ result = result.replace(new RegExp(`props\\.${name}\\b`, 'g'), name);
302
+ }
303
+
304
+ // 3. Remove redeclarations of define:vars variables (already injected as const by Astro)
305
+ for (const name of varNames) {
306
+ result = result.replace(
307
+ new RegExp(`^\\s*(var|let|const)\\s+${name}\\s*=[^;]*;?\\s*$`, 'gm'),
308
+ ''
309
+ );
310
+ }
311
+
312
+ return result;
313
+ }
314
+
315
+ /**
316
+ * Build the script section with proper el/props initialization.
317
+ * - defineVars components: use Astro's define:vars to pass props into inline script
318
+ * - other components: use is:inline to avoid module bundling
319
+ * Both cases use document.currentScript.previousElementSibling to get el.
320
+ */
321
+ function buildScriptSection(
322
+ js: string,
323
+ comp: StructuredComponentDefinition,
324
+ propDefs: Record<string, PropDefinition>
325
+ ): string {
326
+ const elInit = 'const el = document.currentScript.previousElementSibling;';
327
+
328
+ if (comp.defineVars) {
329
+ const vars = comp.defineVars === true
330
+ ? Object.keys(propDefs).filter(k => k !== 'children')
331
+ : comp.defineVars;
332
+
333
+ if (vars.length > 0) {
334
+ const transformedJS = transformDefineVarsJS(js, vars);
335
+ const defineVarsObj = `{ ${vars.join(', ')} }`;
336
+ return `\n<script define:vars={${defineVarsObj}}>\n${elInit}\n${transformedJS}\n</script>\n`;
337
+ }
338
+ }
339
+
340
+ return `\n<script is:inline>\n${elInit}\n${js}\n</script>\n`;
341
+ }