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.
- package/build-static.ts +8 -1
- package/dist/bin/cli.js +1 -1
- package/dist/build-static.js +3 -3
- package/dist/chunks/{chunk-PQ2HRXDR.js → chunk-2MHDV5BF.js} +11 -1
- package/dist/chunks/chunk-2MHDV5BF.js.map +7 -0
- package/dist/chunks/{chunk-YWJJD5D6.js → chunk-A725KYFK.js} +36 -17
- package/dist/chunks/{chunk-YWJJD5D6.js.map → chunk-A725KYFK.js.map} +3 -3
- package/dist/chunks/{chunk-CVLFID6V.js → chunk-CXCBV2M7.js} +65 -14
- package/dist/chunks/chunk-CXCBV2M7.js.map +7 -0
- package/dist/chunks/{chunk-4OFZP5NQ.js → chunk-HNLUO36W.js} +15 -4
- package/dist/chunks/chunk-HNLUO36W.js.map +7 -0
- package/dist/chunks/{chunk-56EUSC6D.js → chunk-LHLHPYSP.js} +4 -4
- package/dist/chunks/{chunk-56EUSC6D.js.map → chunk-LHLHPYSP.js.map} +2 -2
- package/dist/chunks/{configService-VOY2MY2K.js → configService-R3OGU2UD.js} +2 -2
- package/dist/entries/server-router.js +3 -3
- package/dist/lib/server/index.js +5 -5
- package/lib/server/routes/pages.ts +37 -2
- package/lib/server/services/cmsService.ts +21 -0
- package/lib/server/services/configService.ts +20 -0
- package/lib/server/ssr/buildErrorOverlay.ts +22 -4
- package/lib/server/ssr/errorOverlay.ts +11 -3
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +165 -0
- package/lib/server/ssr/htmlGenerator.ts +25 -6
- package/lib/server/ssr/liveReloadIntegration.test.ts +3 -1
- package/lib/server/ssr/metaTagGenerator.ts +54 -6
- package/lib/server/ssr/ssrRenderer.ts +1 -0
- package/lib/server/ssrRenderer.test.ts +157 -2
- package/package.json +1 -1
- package/dist/chunks/chunk-4OFZP5NQ.js.map +0 -7
- package/dist/chunks/chunk-CVLFID6V.js.map +0 -7
- package/dist/chunks/chunk-PQ2HRXDR.js.map +0 -7
- /package/dist/chunks/{configService-VOY2MY2K.js.map → configService-R3OGU2UD.js.map} +0 -0
|
@@ -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 & <stuff>" />`);
|
|
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,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, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\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
|
-
}
|