meno-core 1.0.50 → 1.0.52

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 (32) hide show
  1. package/build-static.ts +8 -1
  2. package/dist/bin/cli.js +1 -1
  3. package/dist/build-static.js +3 -3
  4. package/dist/chunks/{chunk-PQ2HRXDR.js → chunk-2MHDV5BF.js} +11 -1
  5. package/dist/chunks/chunk-2MHDV5BF.js.map +7 -0
  6. package/dist/chunks/{chunk-YWJJD5D6.js → chunk-A725KYFK.js} +36 -17
  7. package/dist/chunks/{chunk-YWJJD5D6.js.map → chunk-A725KYFK.js.map} +3 -3
  8. package/dist/chunks/{chunk-CVLFID6V.js → chunk-CXCBV2M7.js} +65 -14
  9. package/dist/chunks/chunk-CXCBV2M7.js.map +7 -0
  10. package/dist/chunks/{chunk-4OFZP5NQ.js → chunk-HNLUO36W.js} +15 -4
  11. package/dist/chunks/chunk-HNLUO36W.js.map +7 -0
  12. package/dist/chunks/{chunk-56EUSC6D.js → chunk-LHLHPYSP.js} +4 -4
  13. package/dist/chunks/{chunk-56EUSC6D.js.map → chunk-LHLHPYSP.js.map} +2 -2
  14. package/dist/chunks/{configService-VOY2MY2K.js → configService-R3OGU2UD.js} +2 -2
  15. package/dist/entries/server-router.js +3 -3
  16. package/dist/lib/server/index.js +5 -5
  17. package/lib/server/routes/pages.ts +37 -2
  18. package/lib/server/services/cmsService.ts +21 -0
  19. package/lib/server/services/configService.ts +20 -0
  20. package/lib/server/ssr/buildErrorOverlay.ts +22 -4
  21. package/lib/server/ssr/errorOverlay.ts +11 -3
  22. package/lib/server/ssr/htmlGenerator.nonce.test.ts +165 -0
  23. package/lib/server/ssr/htmlGenerator.ts +25 -6
  24. package/lib/server/ssr/liveReloadIntegration.test.ts +3 -1
  25. package/lib/server/ssr/metaTagGenerator.ts +54 -6
  26. package/lib/server/ssr/ssrRenderer.ts +1 -0
  27. package/lib/server/ssrRenderer.test.ts +157 -2
  28. package/package.json +1 -1
  29. package/dist/chunks/chunk-4OFZP5NQ.js.map +0 -7
  30. package/dist/chunks/chunk-CVLFID6V.js.map +0 -7
  31. package/dist/chunks/chunk-PQ2HRXDR.js.map +0 -7
  32. /package/dist/chunks/{configService-VOY2MY2K.js.map → configService-R3OGU2UD.js.map} +0 -0
@@ -1950,6 +1950,7 @@ export async function renderPageSSR(
1950
1950
  slugMappings,
1951
1951
  pagePath,
1952
1952
  baseUrl,
1953
+ social: configService.getSocial(),
1953
1954
  });
1954
1955
 
1955
1956
  // Resolve title for use in HTML template
@@ -137,15 +137,170 @@ describe("SSR Renderer - generateMetaTags", () => {
137
137
  ogImage: "https://example.com/image.jpg",
138
138
  ogType: "article"
139
139
  };
140
-
140
+
141
141
  const tags = generateMetaTags(meta);
142
-
142
+
143
143
  expect(tags).toContain(`<meta property="og:title" content="OG Title" />`);
144
144
  expect(tags).toContain(`<meta property="og:description" content="OG Description" />`);
145
145
  expect(tags).toContain(`<meta property="og:image" content="https://example.com/image.jpg" />`);
146
146
  expect(tags).toContain(`<meta property="og:type" content="article" />`);
147
147
  });
148
148
 
149
+ test("should emit twitter:card=summary_large_image when ogImage present", () => {
150
+ const meta = {
151
+ ogTitle: "OG Title",
152
+ ogDescription: "OG Description",
153
+ ogImage: "https://example.com/image.jpg",
154
+ };
155
+
156
+ const tags = generateMetaTags(meta);
157
+
158
+ expect(tags).toContain(`<meta name="twitter:card" content="summary_large_image" />`);
159
+ expect(tags).toContain(`<meta name="twitter:title" content="OG Title" />`);
160
+ expect(tags).toContain(`<meta name="twitter:description" content="OG Description" />`);
161
+ });
162
+
163
+ test("should emit twitter:card=summary when no ogImage", () => {
164
+ const meta = {
165
+ title: "Plain Title",
166
+ description: "Plain Description",
167
+ };
168
+
169
+ const tags = generateMetaTags(meta);
170
+
171
+ expect(tags).toContain(`<meta name="twitter:card" content="summary" />`);
172
+ });
173
+
174
+ test("twitter:title falls back to title when ogTitle is absent", () => {
175
+ const meta = {
176
+ title: "Plain Title",
177
+ ogImage: "https://example.com/image.jpg",
178
+ };
179
+
180
+ const tags = generateMetaTags(meta);
181
+
182
+ expect(tags).toContain(`<meta name="twitter:title" content="Plain Title" />`);
183
+ });
184
+
185
+ test("twitter:description falls back to description when ogDescription is absent", () => {
186
+ const meta = {
187
+ description: "Plain Description",
188
+ ogImage: "https://example.com/image.jpg",
189
+ };
190
+
191
+ const tags = generateMetaTags(meta);
192
+
193
+ expect(tags).toContain(`<meta name="twitter:description" content="Plain Description" />`);
194
+ });
195
+
196
+ test("should emit twitter:site and twitter:creator with @-normalized handle", () => {
197
+ const meta = { title: "Page" };
198
+
199
+ const tags = generateMetaTags(meta, '', 'en', undefined, {
200
+ social: { twitterHandle: 'meno' },
201
+ });
202
+
203
+ expect(tags).toContain(`<meta name="twitter:site" content="@meno" />`);
204
+ expect(tags).toContain(`<meta name="twitter:creator" content="@meno" />`);
205
+ });
206
+
207
+ test("should not double-prefix handle that already starts with @", () => {
208
+ const meta = { title: "Page" };
209
+
210
+ const tags = generateMetaTags(meta, '', 'en', undefined, {
211
+ social: { twitterHandle: '@meno' },
212
+ });
213
+
214
+ expect(tags).toContain(`<meta name="twitter:site" content="@meno" />`);
215
+ expect(tags).not.toContain(`@@meno`);
216
+ });
217
+
218
+ test("should emit no twitter tags when meta is empty", () => {
219
+ const tags = generateMetaTags({});
220
+
221
+ expect(tags).not.toContain('twitter:');
222
+ });
223
+
224
+ test("should escape HTML in twitter content", () => {
225
+ const meta = {
226
+ ogTitle: "Title & <stuff>",
227
+ };
228
+
229
+ const tags = generateMetaTags(meta);
230
+
231
+ expect(tags).toContain(`<meta name="twitter:title" content="Title &amp; &lt;stuff&gt;" />`);
232
+ });
233
+
234
+ test("og:image swaps /images/*.webp → .jpg", () => {
235
+ const meta = { ogImage: "/images/photo.webp" };
236
+
237
+ const tags = generateMetaTags(meta);
238
+
239
+ expect(tags).toContain(`<meta property="og:image" content="/images/photo.jpg" />`);
240
+ });
241
+
242
+ test("og:image swaps /images/*.avif → .jpg", () => {
243
+ const meta = { ogImage: "/images/photo.avif" };
244
+
245
+ const tags = generateMetaTags(meta);
246
+
247
+ expect(tags).toContain(`<meta property="og:image" content="/images/photo.jpg" />`);
248
+ });
249
+
250
+ test("og:image is made absolute when options.baseUrl is provided", () => {
251
+ const meta = { ogImage: "/images/photo.webp" };
252
+
253
+ const tags = generateMetaTags(meta, '', 'en', undefined, {
254
+ baseUrl: 'https://example.com',
255
+ });
256
+
257
+ expect(tags).toContain(`<meta property="og:image" content="https://example.com/images/photo.jpg" />`);
258
+ });
259
+
260
+ test("og:image strips trailing slash from baseUrl", () => {
261
+ const meta = { ogImage: "/images/photo.webp" };
262
+
263
+ const tags = generateMetaTags(meta, '', 'en', undefined, {
264
+ baseUrl: 'https://example.com/',
265
+ });
266
+
267
+ expect(tags).toContain(`<meta property="og:image" content="https://example.com/images/photo.jpg" />`);
268
+ });
269
+
270
+ test("og:image preserves .png unchanged (other than baseUrl absolutization)", () => {
271
+ const meta = { ogImage: "/images/photo.png" };
272
+
273
+ const tags = generateMetaTags(meta);
274
+
275
+ expect(tags).toContain(`<meta property="og:image" content="/images/photo.png" />`);
276
+ });
277
+
278
+ test("og:image preserves .jpg unchanged", () => {
279
+ const meta = { ogImage: "/images/photo.jpg" };
280
+
281
+ const tags = generateMetaTags(meta);
282
+
283
+ expect(tags).toContain(`<meta property="og:image" content="/images/photo.jpg" />`);
284
+ });
285
+
286
+ test("og:image preserves external absolute URL unchanged", () => {
287
+ const meta = { ogImage: "https://cdn.example.com/photo.webp" };
288
+
289
+ const tags = generateMetaTags(meta);
290
+
291
+ expect(tags).toContain(`<meta property="og:image" content="https://cdn.example.com/photo.webp" />`);
292
+ });
293
+
294
+ test("og:image does not rewrite non-/images/ local paths but still absolutizes them", () => {
295
+ const meta = { ogImage: "/cdn-cgi/photo.webp" };
296
+
297
+ const tags = generateMetaTags(meta, '', 'en', undefined, {
298
+ baseUrl: 'https://example.com',
299
+ });
300
+
301
+ expect(tags).toContain(`<meta property="og:image" content="https://example.com/cdn-cgi/photo.webp" />`);
302
+ });
303
+
149
304
  test("should generate canonical URL", () => {
150
305
  const meta = {};
151
306
  const url = "https://example.com/page";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meno-core",
3
- "version": "1.0.50",
3
+ "version": "1.0.52",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "meno": "./dist/bin/cli.js"
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../lib/server/ssr/buildErrorOverlay.ts"],
4
- "sourcesContent": ["/**\n * Build Error Overlay Generator\n * Generates an HTML page showing build errors when the static server detects _errors.json\n */\n\nexport interface BuildError {\n file: string; // e.g., \"pages/posts.json\"\n message: string; // Error message\n type: string; // 'minify' | 'render' | 'parse' | 'cms'\n}\n\nexport interface BuildErrorsData {\n errors: BuildError[];\n timestamp: number;\n}\n\n/**\n * Escape HTML to prevent XSS in error messages\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n}\n\n/**\n * Safely encode data for embedding in a script tag\n */\nfunction safeJsonForScript(data: unknown): string {\n return JSON.stringify(data)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026');\n}\n\n/**\n * Generate HTML page showing build errors\n */\nexport function generateBuildErrorPage(data: BuildErrorsData): string {\n const { errors, timestamp } = data;\n const timeStr = new Date(timestamp).toLocaleTimeString();\n\n // Generate plain text for copying to AI\n const plainTextErrors = errors.map(err =>\n `[${err.type.toUpperCase()}] ${err.file}\\n${err.message}`\n ).join('\\n\\n');\n const copyText = `Build failed with ${errors.length} error(s):\\n\\n${plainTextErrors}`;\n\n const errorListHtml = errors.map((err) => `\n <div class=\"error-item\">\n <div class=\"error-item-header\">\n <div class=\"error-type\">${escapeHtml(err.type)}</div>\n <div class=\"error-file\">${escapeHtml(err.file)}</div>\n </div>\n <div class=\"error-message\">${escapeHtml(err.message)}</div>\n </div>\n `).join('');\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Build Failed</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 100%);\n color: #e2e8f0;\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n }\n\n .container {\n max-width: 720px;\n width: 100%;\n }\n\n .card {\n background: rgba(30, 30, 50, 0.8);\n backdrop-filter: blur(20px);\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 16px;\n overflow: hidden;\n box-shadow:\n 0 4px 6px rgba(0, 0, 0, 0.1),\n 0 20px 50px rgba(0, 0, 0, 0.3),\n inset 0 1px 0 rgba(255, 255, 255, 0.05);\n }\n\n .header {\n background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);\n padding: 20px 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-icon {\n width: 32px;\n height: 32px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .header-icon svg {\n width: 18px;\n height: 18px;\n stroke: white;\n }\n\n .header-title {\n font-size: 16px;\n font-weight: 600;\n letter-spacing: -0.01em;\n }\n\n .error-count {\n background: rgba(0, 0, 0, 0.25);\n padding: 6px 14px;\n border-radius: 100px;\n font-size: 13px;\n font-weight: 500;\n }\n\n .body {\n padding: 20px;\n max-height: 50vh;\n overflow-y: auto;\n }\n\n .body::-webkit-scrollbar {\n width: 6px;\n }\n\n .body::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .body::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.1);\n border-radius: 3px;\n }\n\n .error-item {\n background: rgba(0, 0, 0, 0.3);\n border: 1px solid rgba(255, 255, 255, 0.06);\n border-radius: 10px;\n padding: 14px 16px;\n margin-bottom: 10px;\n transition: border-color 0.15s;\n }\n\n .error-item:last-child { margin-bottom: 0; }\n\n .error-item:hover {\n border-color: rgba(255, 255, 255, 0.12);\n }\n\n .error-item-header {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 10px;\n }\n\n .error-type {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n padding: 4px 8px;\n border-radius: 4px;\n background: rgba(239, 68, 68, 0.2);\n color: #fca5a5;\n }\n\n .error-file {\n font-family: 'SF Mono', 'Fira Code', Menlo, Monaco, monospace;\n font-size: 12px;\n color: #94a3b8;\n }\n\n .error-message {\n font-family: 'SF Mono', 'Fira Code', Menlo, Monaco, monospace;\n font-size: 12px;\n line-height: 1.7;\n color: #f87171;\n white-space: pre-wrap;\n word-break: break-word;\n background: rgba(0, 0, 0, 0.2);\n padding: 10px 12px;\n border-radius: 6px;\n border-left: 2px solid #dc2626;\n }\n\n .footer {\n padding: 16px 20px;\n background: rgba(0, 0, 0, 0.2);\n border-top: 1px solid rgba(255, 255, 255, 0.06);\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n }\n\n .footer-info {\n font-size: 12px;\n color: #64748b;\n }\n\n .footer-actions {\n display: flex;\n gap: 8px;\n }\n\n .btn {\n border: none;\n padding: 10px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.15s;\n }\n\n .btn svg {\n width: 14px;\n height: 14px;\n }\n\n .btn-secondary {\n background: rgba(255, 255, 255, 0.08);\n color: #e2e8f0;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .btn-secondary:hover {\n background: rgba(255, 255, 255, 0.12);\n border-color: rgba(255, 255, 255, 0.15);\n }\n\n .btn-primary {\n background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);\n color: white;\n }\n\n .btn-primary:hover {\n background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);\n }\n\n .btn-success {\n background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"card\">\n <div class=\"header\">\n <div class=\"header-left\">\n <div class=\"header-icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"></line>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"></line>\n </svg>\n </div>\n <span class=\"header-title\">Build Failed</span>\n </div>\n <span class=\"error-count\">${errors.length} error${errors.length === 1 ? '' : 's'}</span>\n </div>\n <div class=\"body\">\n ${errorListHtml}\n </div>\n <div class=\"footer\">\n <div class=\"footer-info\">Failed at ${timeStr}</div>\n <div class=\"footer-actions\">\n <button class=\"btn btn-secondary\" id=\"copyBtn\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"></rect>\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"></path>\n </svg>\n <span>Copy</span>\n </button>\n <button class=\"btn btn-primary\" onclick=\"location.reload()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"23 4 23 10 17 10\"></polyline>\n <path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\"></path>\n </svg>\n Refresh\n </button>\n </div>\n </div>\n </div>\n </div>\n <script>\n (function() {\n var copyBtn = document.getElementById('copyBtn');\n var copyText = ${safeJsonForScript(copyText)};\n\n copyBtn.addEventListener('click', function() {\n navigator.clipboard.writeText(copyText).then(function() {\n var span = copyBtn.querySelector('span');\n var original = span.textContent;\n span.textContent = 'Copied!';\n copyBtn.classList.add('btn-success');\n setTimeout(function() {\n span.textContent = original;\n copyBtn.classList.remove('btn-success');\n }, 2000);\n }).catch(function(err) {\n console.error('Failed to copy:', err);\n });\n });\n })();\n </script>\n</body>\n</html>`;\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }