meno-core 1.0.7 → 1.0.9

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.
package/build-static.ts CHANGED
@@ -150,6 +150,37 @@ function getDisplayPath(
150
150
  return slug === "" ? `/${locale}` : `/${locale}/${slug}`;
151
151
  }
152
152
 
153
+ /**
154
+ * Generate robots.txt with sensible defaults
155
+ */
156
+ async function generateRobotsTxt(siteUrl: string, distDir: string): Promise<void> {
157
+ const content = `User-agent: *
158
+ Allow: /
159
+
160
+ Sitemap: ${siteUrl}/sitemap.xml
161
+ `;
162
+ await writeFile(join(distDir, 'robots.txt'), content, 'utf-8');
163
+ }
164
+
165
+ /**
166
+ * Generate sitemap.xml from collected URLs
167
+ */
168
+ async function generateSitemap(urls: string[], siteUrl: string, distDir: string): Promise<void> {
169
+ // Sort URLs for deterministic output
170
+ const sortedUrls = [...urls].sort();
171
+
172
+ const urlEntries = sortedUrls
173
+ .map(url => ` <url><loc>${siteUrl}${url}</loc></url>`)
174
+ .join('\n');
175
+
176
+ const content = `<?xml version="1.0" encoding="UTF-8"?>
177
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
178
+ ${urlEntries}
179
+ </urlset>
180
+ `;
181
+ await writeFile(join(distDir, 'sitemap.xml'), content, 'utf-8');
182
+ }
183
+
153
184
  /**
154
185
  * Clean dist directory, keeping only production files
155
186
  */
@@ -232,7 +263,9 @@ async function buildCMSTemplates(
232
263
  i18nConfig: I18nConfig,
233
264
  slugMappings: SlugMap[],
234
265
  distDir: string,
235
- cmsService: CMSService
266
+ cmsService: CMSService,
267
+ generatedUrls: Set<string>,
268
+ siteUrl?: string
236
269
  ): Promise<{ success: number; errors: number }> {
237
270
  let successCount = 0;
238
271
  let errorCount = 0;
@@ -277,7 +310,7 @@ async function buildCMSTemplates(
277
310
  for (const item of items) {
278
311
  for (const localeConfig of i18nConfig.locales) {
279
312
  const locale = localeConfig.code;
280
- const baseUrl = "";
313
+ const baseUrl = siteUrl || "";
281
314
  const itemPath = buildCMSItemPath(cmsSchema.urlPattern, item, cmsSchema.slugField, locale, i18nConfig);
282
315
 
283
316
  // Generate HTML with JS returned separately (CSP-compliant)
@@ -313,6 +346,7 @@ async function buildCMSTemplates(
313
346
  await writeFile(outputPath, finalHtml, 'utf-8');
314
347
 
315
348
  const displayPath = locale === i18nConfig.defaultLocale ? itemPath : `/${locale}${itemPath}`;
349
+ generatedUrls.add(displayPath);
316
350
  console.log(` āœ… ${displayPath}`);
317
351
  successCount++;
318
352
  }
@@ -333,7 +367,11 @@ async function buildStaticPages(): Promise<void> {
333
367
  console.log("šŸ—ļø Building static HTML files...\n");
334
368
 
335
369
  // Load project config first
336
- await loadProjectConfig();
370
+ const projectConfig = await loadProjectConfig();
371
+ const siteUrl = (projectConfig as { siteUrl?: string }).siteUrl?.replace(/\/$/, ''); // Remove trailing slash
372
+
373
+ // Track all generated URLs for sitemap
374
+ const generatedUrls = new Set<string>();
337
375
 
338
376
  // Load i18n config for multi-locale build
339
377
  const i18nConfig = await loadI18nConfig();
@@ -446,7 +484,7 @@ async function buildStaticPages(): Promise<void> {
446
484
  // Generate HTML for each locale
447
485
  for (const localeConfig of i18nConfig.locales) {
448
486
  const locale = localeConfig.code;
449
- const baseUrl = ""; // Empty for relative paths
487
+ const baseUrl = siteUrl || "";
450
488
 
451
489
  // Build the URL path that will be used for this locale
452
490
  const urlPath = getDisplayPath(basePath, locale, i18nConfig.defaultLocale, slugs);
@@ -483,6 +521,7 @@ async function buildStaticPages(): Promise<void> {
483
521
 
484
522
  await writeFile(outputPath, finalHtml, "utf-8");
485
523
 
524
+ generatedUrls.add(urlPath);
486
525
  console.log(`āœ… Built: ${urlPath} → ${outputPath}`);
487
526
  successCount++;
488
527
  }
@@ -501,11 +540,22 @@ async function buildStaticPages(): Promise<void> {
501
540
  i18nConfig,
502
541
  slugMappings,
503
542
  distDir,
504
- cmsService
543
+ cmsService,
544
+ generatedUrls,
545
+ siteUrl
505
546
  );
506
547
  successCount += cmsResult.success;
507
548
  errorCount += cmsResult.errors;
508
549
 
550
+ // Generate SEO files (robots.txt and sitemap.xml)
551
+ if (siteUrl) {
552
+ await generateRobotsTxt(siteUrl, distDir);
553
+ await generateSitemap([...generatedUrls], siteUrl, distDir);
554
+ console.log(`\nšŸ” SEO files generated (robots.txt, sitemap.xml)`);
555
+ } else {
556
+ console.warn(`\nāš ļø Skipping SEO files: siteUrl not configured in project.config.json`);
557
+ }
558
+
509
559
  console.log("\n" + "=".repeat(50));
510
560
  console.log(`✨ Build complete!`);
511
561
  console.log(` āœ… Success: ${successCount}`);
@@ -520,6 +570,9 @@ async function buildStaticPages(): Promise<void> {
520
570
  if (existsSync(functionsDir)) {
521
571
  console.log(` - functions/ (Cloudflare Pages Functions)`);
522
572
  }
573
+ if (siteUrl) {
574
+ console.log(` - robots.txt, sitemap.xml (SEO)`);
575
+ }
523
576
  console.log(` - No React, no client-router āœ“`);
524
577
  }
525
578
 
@@ -6,10 +6,12 @@ export interface FontConfig {
6
6
  weight?: number;
7
7
  weightMax?: number; // If set, font is variable with weight range [weight, weightMax]
8
8
  style?: 'normal' | 'italic';
9
+ fontDisplay?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
9
10
  }
10
11
 
11
12
  interface ProjectConfig {
12
13
  fonts?: FontConfig[];
14
+ siteUrl?: string;
13
15
  [key: string]: unknown;
14
16
  }
15
17
 
@@ -67,6 +69,7 @@ export function generateFontCSS(): string {
67
69
  const weight = font.weight ?? 400;
68
70
  const weightMax = font.weightMax;
69
71
  const style = font.style ?? 'normal';
72
+ const fontDisplay = font.fontDisplay;
70
73
 
71
74
  // Variable fonts use weight range syntax: "100 900"
72
75
  const fontWeight = weightMax != null ? `${weight} ${weightMax}` : weight;
@@ -75,7 +78,7 @@ export function generateFontCSS(): string {
75
78
  font-family: '${family}';
76
79
  src: url('${font.path}') format('${format}');
77
80
  font-weight: ${fontWeight};
78
- font-style: ${style};
81
+ font-style: ${style};${fontDisplay ? `\n font-display: ${fontDisplay};` : ''}
79
82
  }`;
80
83
  })
81
84
  .join('\n\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meno-core",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "meno": "./bin/cli.ts"