@ampless/runtime 0.2.0-alpha.8 → 1.0.0-alpha.13
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/README.ja.md +86 -0
- package/README.md +4 -1
- package/dist/dispatchers/index.d.ts +21 -11
- package/dist/dispatchers/index.js +10 -20
- package/dist/index.d.ts +29 -26
- package/dist/index.js +174 -95
- package/dist/middleware.d.ts +16 -14
- package/dist/middleware.js +8 -17
- package/dist/routes/index.d.ts +48 -74
- package/dist/routes/index.js +84 -79
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -20,35 +20,20 @@ function createStorage(outputs) {
|
|
|
20
20
|
// src/posts.ts
|
|
21
21
|
import { cookies } from "next/headers";
|
|
22
22
|
import { generateServerClientUsingCookies } from "@aws-amplify/adapter-nextjs/api";
|
|
23
|
-
|
|
24
|
-
if (typeof value !== "string") return value;
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(value);
|
|
27
|
-
} catch {
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
23
|
+
import { decodeAwsJson } from "ampless";
|
|
31
24
|
function decodeMetadata(value) {
|
|
32
25
|
if (value === null || value === void 0) return void 0;
|
|
33
|
-
const parsed =
|
|
26
|
+
const parsed = decodeAwsJson(value);
|
|
34
27
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
35
28
|
}
|
|
36
|
-
function safeJsonParse(value) {
|
|
37
|
-
try {
|
|
38
|
-
return JSON.parse(value);
|
|
39
|
-
} catch {
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
29
|
function toCorePost(p) {
|
|
44
30
|
return {
|
|
45
31
|
postId: p.postId,
|
|
46
|
-
siteId: p.siteId,
|
|
47
32
|
slug: p.slug,
|
|
48
33
|
title: p.title,
|
|
49
34
|
excerpt: p.excerpt ?? void 0,
|
|
50
35
|
format: p.format ?? "markdown",
|
|
51
|
-
body:
|
|
36
|
+
body: decodeAwsJson(p.body),
|
|
52
37
|
status: p.status ?? "draft",
|
|
53
38
|
publishedAt: p.publishedAt ?? void 0,
|
|
54
39
|
tags: (p.tags ?? []).filter((t) => typeof t === "string"),
|
|
@@ -66,7 +51,6 @@ function createPostsApi(outputs) {
|
|
|
66
51
|
return {
|
|
67
52
|
async listPublishedPosts(opts = {}) {
|
|
68
53
|
const { data, errors } = await client.queries.listPublishedPosts({
|
|
69
|
-
siteId: opts.siteId ?? "default",
|
|
70
54
|
from: opts.from,
|
|
71
55
|
to: opts.to,
|
|
72
56
|
limit: opts.limit ?? 20,
|
|
@@ -76,9 +60,8 @@ function createPostsApi(outputs) {
|
|
|
76
60
|
const items = (data?.items ?? []).filter((p) => p !== null).map(toCorePost);
|
|
77
61
|
return { items, nextToken: data?.nextToken ?? null };
|
|
78
62
|
},
|
|
79
|
-
async getPublishedPost(slug
|
|
63
|
+
async getPublishedPost(slug) {
|
|
80
64
|
const { data, errors } = await client.queries.getPublishedPost({
|
|
81
|
-
siteId: opts.siteId ?? "default",
|
|
82
65
|
slug
|
|
83
66
|
});
|
|
84
67
|
if (errors) throw new Error(errors[0]?.message ?? "Failed to get post");
|
|
@@ -86,7 +69,6 @@ function createPostsApi(outputs) {
|
|
|
86
69
|
},
|
|
87
70
|
async listPostsByTag(tag, opts = {}) {
|
|
88
71
|
const { data, errors } = await client.queries.listPostsByTag({
|
|
89
|
-
siteId: opts.siteId ?? "default",
|
|
90
72
|
tag,
|
|
91
73
|
limit: opts.limit ?? 20,
|
|
92
74
|
nextToken: opts.nextToken
|
|
@@ -99,25 +81,27 @@ function createPostsApi(outputs) {
|
|
|
99
81
|
}
|
|
100
82
|
|
|
101
83
|
// src/site-settings.ts
|
|
102
|
-
import {
|
|
84
|
+
import { unflattenSettings } from "ampless";
|
|
103
85
|
function createSiteSettings(cmsConfig, storage) {
|
|
104
|
-
async function fetchRemote(
|
|
86
|
+
async function fetchRemote() {
|
|
105
87
|
if (!storage.isStorageConfigured()) return null;
|
|
106
88
|
let url;
|
|
107
89
|
try {
|
|
108
|
-
url = storage.publicAssetUrl(
|
|
90
|
+
url = storage.publicAssetUrl("public/site-settings.json");
|
|
109
91
|
} catch {
|
|
110
92
|
return null;
|
|
111
93
|
}
|
|
112
|
-
const res = await fetch(url, {
|
|
94
|
+
const res = await fetch(url, {
|
|
95
|
+
next: { revalidate: 60, tags: ["site-settings"] }
|
|
96
|
+
});
|
|
113
97
|
if (!res.ok) return null;
|
|
114
98
|
const flat = await res.json();
|
|
115
99
|
return unflattenSettings(flat);
|
|
116
100
|
}
|
|
117
101
|
return {
|
|
118
|
-
async loadSiteSettings(
|
|
119
|
-
const remote = await fetchRemote(
|
|
120
|
-
const baseSite =
|
|
102
|
+
async loadSiteSettings() {
|
|
103
|
+
const remote = await fetchRemote().catch(() => null);
|
|
104
|
+
const baseSite = cmsConfig.site;
|
|
121
105
|
return {
|
|
122
106
|
site: {
|
|
123
107
|
name: remote?.site?.name ?? baseSite.name,
|
|
@@ -142,15 +126,14 @@ function createSiteSettings(cmsConfig, storage) {
|
|
|
142
126
|
}
|
|
143
127
|
|
|
144
128
|
// src/seo.ts
|
|
145
|
-
import { DEFAULT_SITE_ID as DEFAULT_SITE_ID2 } from "ampless";
|
|
146
129
|
function isPlugin(p) {
|
|
147
130
|
return typeof p === "object" && p !== null && "apiVersion" in p;
|
|
148
131
|
}
|
|
149
132
|
function createSeo(cmsConfig, settingsApi) {
|
|
150
133
|
const plugins = (cmsConfig.plugins ?? []).filter(isPlugin);
|
|
151
134
|
return {
|
|
152
|
-
async postMetadata(post
|
|
153
|
-
const settings = await settingsApi.loadSiteSettings(
|
|
135
|
+
async postMetadata(post) {
|
|
136
|
+
const settings = await settingsApi.loadSiteSettings();
|
|
154
137
|
const accum = {};
|
|
155
138
|
for (const plugin of plugins) {
|
|
156
139
|
if (!plugin.metadata) continue;
|
|
@@ -167,8 +150,8 @@ function createSeo(cmsConfig, settingsApi) {
|
|
|
167
150
|
}
|
|
168
151
|
return accum;
|
|
169
152
|
},
|
|
170
|
-
async siteMetadata(
|
|
171
|
-
const settings = await settingsApi.loadSiteSettings(
|
|
153
|
+
async siteMetadata() {
|
|
154
|
+
const settings = await settingsApi.loadSiteSettings();
|
|
172
155
|
const accum = {
|
|
173
156
|
title: settings.site.name,
|
|
174
157
|
description: settings.site.description
|
|
@@ -193,24 +176,25 @@ function createSeo(cmsConfig, settingsApi) {
|
|
|
193
176
|
|
|
194
177
|
// src/theme-active.ts
|
|
195
178
|
import { headers } from "next/headers";
|
|
196
|
-
import { DEFAULT_SITE_ID as DEFAULT_SITE_ID3 } from "ampless";
|
|
197
179
|
function createThemeActive(registry, storage) {
|
|
198
|
-
async function fetchActiveFromCache(
|
|
180
|
+
async function fetchActiveFromCache() {
|
|
199
181
|
if (!storage.isStorageConfigured()) return null;
|
|
200
182
|
let url;
|
|
201
183
|
try {
|
|
202
|
-
url = storage.publicAssetUrl(
|
|
184
|
+
url = storage.publicAssetUrl("public/site-settings.json");
|
|
203
185
|
} catch {
|
|
204
186
|
return null;
|
|
205
187
|
}
|
|
206
|
-
const res = await fetch(url, {
|
|
188
|
+
const res = await fetch(url, {
|
|
189
|
+
next: { revalidate: 60, tags: ["site-settings"] }
|
|
190
|
+
});
|
|
207
191
|
if (!res.ok) return null;
|
|
208
192
|
const flat = await res.json();
|
|
209
193
|
const v = flat["theme.active"];
|
|
210
194
|
return typeof v === "string" ? v : null;
|
|
211
195
|
}
|
|
212
196
|
return {
|
|
213
|
-
async resolveActiveTheme(
|
|
197
|
+
async resolveActiveTheme() {
|
|
214
198
|
let previewOverride = null;
|
|
215
199
|
try {
|
|
216
200
|
const h = await headers();
|
|
@@ -221,7 +205,7 @@ function createThemeActive(registry, storage) {
|
|
|
221
205
|
const mod2 = registry.themes[previewOverride];
|
|
222
206
|
if (mod2) return { name: previewOverride, module: mod2 };
|
|
223
207
|
}
|
|
224
|
-
const stored = await fetchActiveFromCache(
|
|
208
|
+
const stored = await fetchActiveFromCache().catch(() => null);
|
|
225
209
|
const name = stored && stored in registry.themes ? stored : registry.defaultTheme;
|
|
226
210
|
const mod = registry.themes[name] ?? registry.themes[registry.defaultTheme];
|
|
227
211
|
if (!mod) {
|
|
@@ -236,7 +220,6 @@ function createThemeActive(registry, storage) {
|
|
|
236
220
|
|
|
237
221
|
// src/theme-config.ts
|
|
238
222
|
import {
|
|
239
|
-
DEFAULT_SITE_ID as DEFAULT_SITE_ID4,
|
|
240
223
|
resolveThemeValues,
|
|
241
224
|
themeSettingKey
|
|
242
225
|
} from "ampless";
|
|
@@ -247,23 +230,23 @@ function validateColorScheme(raw) {
|
|
|
247
230
|
return DEFAULT_COLOR_SCHEME;
|
|
248
231
|
}
|
|
249
232
|
function createThemeConfig(themeActive, storage) {
|
|
250
|
-
async function fetchRemote(
|
|
233
|
+
async function fetchRemote() {
|
|
251
234
|
if (!storage.isStorageConfigured()) return null;
|
|
252
235
|
let url;
|
|
253
236
|
try {
|
|
254
|
-
url = storage.publicAssetUrl(
|
|
237
|
+
url = storage.publicAssetUrl("public/site-settings.json");
|
|
255
238
|
} catch {
|
|
256
239
|
return null;
|
|
257
240
|
}
|
|
258
|
-
const res = await fetch(url, { next: { revalidate: 60, tags: [
|
|
241
|
+
const res = await fetch(url, { next: { revalidate: 60, tags: ["site-settings"] } });
|
|
259
242
|
if (!res.ok) return null;
|
|
260
243
|
return await res.json();
|
|
261
244
|
}
|
|
262
245
|
return {
|
|
263
|
-
async loadThemeConfig(
|
|
246
|
+
async loadThemeConfig() {
|
|
264
247
|
const [active, flat] = await Promise.all([
|
|
265
|
-
themeActive.resolveActiveTheme(
|
|
266
|
-
fetchRemote(
|
|
248
|
+
themeActive.resolveActiveTheme(),
|
|
249
|
+
fetchRemote().catch(() => null)
|
|
267
250
|
]);
|
|
268
251
|
const manifest = active.module.manifest;
|
|
269
252
|
const stored = {};
|
|
@@ -299,9 +282,17 @@ ${lines.join("\n")}
|
|
|
299
282
|
}
|
|
300
283
|
|
|
301
284
|
// src/rendering.ts
|
|
285
|
+
import { marked } from "marked";
|
|
302
286
|
function escape(s) {
|
|
303
287
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
304
288
|
}
|
|
289
|
+
function textAlignStyle(attrs) {
|
|
290
|
+
const v = attrs?.textAlign;
|
|
291
|
+
if (v === "left" || v === "center" || v === "right" || v === "justify") {
|
|
292
|
+
return ` style="text-align: ${v}"`;
|
|
293
|
+
}
|
|
294
|
+
return "";
|
|
295
|
+
}
|
|
305
296
|
function renderTiptap(node) {
|
|
306
297
|
if (node.type === "text") {
|
|
307
298
|
let html = escape(node.text ?? "");
|
|
@@ -310,6 +301,8 @@ function renderTiptap(node) {
|
|
|
310
301
|
else if (mark.type === "italic") html = `<em>${html}</em>`;
|
|
311
302
|
else if (mark.type === "code") html = `<code>${html}</code>`;
|
|
312
303
|
else if (mark.type === "strike") html = `<s>${html}</s>`;
|
|
304
|
+
else if (mark.type === "underline") html = `<u>${html}</u>`;
|
|
305
|
+
else if (mark.type === "highlight") html = `<mark>${html}</mark>`;
|
|
313
306
|
else if (mark.type === "link") {
|
|
314
307
|
const href = escape(String(mark.attrs?.href ?? "#"));
|
|
315
308
|
html = `<a href="${href}" target="_blank" rel="noopener">${html}</a>`;
|
|
@@ -322,10 +315,10 @@ function renderTiptap(node) {
|
|
|
322
315
|
case "doc":
|
|
323
316
|
return children;
|
|
324
317
|
case "paragraph":
|
|
325
|
-
return `<p>${children}</p>`;
|
|
318
|
+
return `<p${textAlignStyle(node.attrs)}>${children}</p>`;
|
|
326
319
|
case "heading": {
|
|
327
320
|
const level = Number(node.attrs?.level ?? 1);
|
|
328
|
-
return `<h${level}>${children}</h${level}>`;
|
|
321
|
+
return `<h${level}${textAlignStyle(node.attrs)}>${children}</h${level}>`;
|
|
329
322
|
}
|
|
330
323
|
case "bulletList":
|
|
331
324
|
return `<ul>${children}</ul>`;
|
|
@@ -350,52 +343,39 @@ function renderTiptap(node) {
|
|
|
350
343
|
const display = node.attrs?.display ? ` data-display="${escape(String(node.attrs.display))}"` : "";
|
|
351
344
|
return `<img src="${src}" alt="${alt}"${title}${display} loading="lazy" />`;
|
|
352
345
|
}
|
|
346
|
+
case "table":
|
|
347
|
+
return `<table class="tiptap-table"><tbody>${children}</tbody></table>`;
|
|
348
|
+
case "tableRow":
|
|
349
|
+
return `<tr>${children}</tr>`;
|
|
350
|
+
case "tableHeader":
|
|
351
|
+
return `<th${tableCellAttrs(node.attrs)}>${children}</th>`;
|
|
352
|
+
case "tableCell":
|
|
353
|
+
return `<td${tableCellAttrs(node.attrs)}>${children}</td>`;
|
|
354
|
+
case "taskList":
|
|
355
|
+
return `<ul data-type="taskList">${children}</ul>`;
|
|
356
|
+
case "taskItem": {
|
|
357
|
+
const checked = node.attrs?.checked === true ? "true" : "false";
|
|
358
|
+
return `<li data-type="taskItem" data-checked="${checked}">${children}</li>`;
|
|
359
|
+
}
|
|
353
360
|
default:
|
|
354
361
|
return children;
|
|
355
362
|
}
|
|
356
363
|
}
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
out.push(`<h2>${escape(line.slice(3))}</h2>`);
|
|
368
|
-
i++;
|
|
369
|
-
} else if (line.startsWith("```")) {
|
|
370
|
-
const lang = line.slice(3).trim();
|
|
371
|
-
const code = [];
|
|
372
|
-
i++;
|
|
373
|
-
while (i < lines.length && !(lines[i] ?? "").startsWith("```")) {
|
|
374
|
-
code.push(lines[i] ?? "");
|
|
375
|
-
i++;
|
|
376
|
-
}
|
|
377
|
-
i++;
|
|
378
|
-
out.push(
|
|
379
|
-
`<pre><code${lang ? ` class="language-${escape(lang)}"` : ""}>${escape(code.join("\n"))}</code></pre>`
|
|
380
|
-
);
|
|
381
|
-
} else if (line.startsWith("- ")) {
|
|
382
|
-
const items = [];
|
|
383
|
-
while (i < lines.length && (lines[i] ?? "").startsWith("- ")) {
|
|
384
|
-
items.push(`<li>${renderInlineMarkdown((lines[i] ?? "").slice(2))}</li>`);
|
|
385
|
-
i++;
|
|
386
|
-
}
|
|
387
|
-
out.push(`<ul>${items.join("")}</ul>`);
|
|
388
|
-
} else if (line.trim() === "") {
|
|
389
|
-
i++;
|
|
390
|
-
} else {
|
|
391
|
-
out.push(`<p>${renderInlineMarkdown(line)}</p>`);
|
|
392
|
-
i++;
|
|
393
|
-
}
|
|
364
|
+
function tableCellAttrs(attrs) {
|
|
365
|
+
let out = "";
|
|
366
|
+
const colspan = Number(attrs?.colspan ?? 1);
|
|
367
|
+
if (colspan > 1) out += ` colspan="${colspan}"`;
|
|
368
|
+
const rowspan = Number(attrs?.rowspan ?? 1);
|
|
369
|
+
if (rowspan > 1) out += ` rowspan="${rowspan}"`;
|
|
370
|
+
const colwidth = attrs?.colwidth;
|
|
371
|
+
if (Array.isArray(colwidth) && colwidth.length > 0) {
|
|
372
|
+
const w = Number(colwidth[0]);
|
|
373
|
+
if (Number.isFinite(w) && w > 0) out += ` style="width: ${w}px"`;
|
|
394
374
|
}
|
|
395
|
-
return out
|
|
375
|
+
return out;
|
|
396
376
|
}
|
|
397
|
-
function
|
|
398
|
-
return
|
|
377
|
+
function renderMarkdown(md) {
|
|
378
|
+
return marked.parse(md, { gfm: true, breaks: false, async: false });
|
|
399
379
|
}
|
|
400
380
|
function renderBody(post) {
|
|
401
381
|
if (post.format === "html") return String(post.body);
|
|
@@ -426,6 +406,8 @@ function tiptapNodeToMarkdown(node) {
|
|
|
426
406
|
else if (mark.type === "italic") txt = `*${txt}*`;
|
|
427
407
|
else if (mark.type === "code") txt = `\`${txt}\``;
|
|
428
408
|
else if (mark.type === "strike") txt = `~~${txt}~~`;
|
|
409
|
+
else if (mark.type === "underline") txt = `<u>${txt}</u>`;
|
|
410
|
+
else if (mark.type === "highlight") txt = `<mark>${txt}</mark>`;
|
|
429
411
|
else if (mark.type === "link") txt = `[${txt}](${String(mark.attrs?.href ?? "#")})`;
|
|
430
412
|
}
|
|
431
413
|
return txt;
|
|
@@ -463,12 +445,54 @@ function tiptapNodeToMarkdown(node) {
|
|
|
463
445
|
const alt = String(node.attrs?.alt ?? "");
|
|
464
446
|
return ``;
|
|
465
447
|
}
|
|
448
|
+
case "table":
|
|
449
|
+
return tiptapTableToMarkdown(node);
|
|
450
|
+
case "taskList":
|
|
451
|
+
return children + "\n";
|
|
452
|
+
case "taskItem": {
|
|
453
|
+
const checked = node.attrs?.checked === true ? "x" : " ";
|
|
454
|
+
const inner = children.replace(/\n+$/, "");
|
|
455
|
+
const [first, ...rest] = inner.split("\n");
|
|
456
|
+
const cont = rest.map((l) => l ? " " + l : l).join("\n");
|
|
457
|
+
return `- [${checked}] ${first ?? ""}${cont ? "\n" + cont : ""}
|
|
458
|
+
`;
|
|
459
|
+
}
|
|
466
460
|
default:
|
|
467
461
|
return children;
|
|
468
462
|
}
|
|
469
463
|
}
|
|
464
|
+
function tiptapTableToMarkdown(node) {
|
|
465
|
+
const rows = node.content ?? [];
|
|
466
|
+
if (rows.length === 0) return "";
|
|
467
|
+
const renderedRows = [];
|
|
468
|
+
let headerIdx = -1;
|
|
469
|
+
for (let i = 0; i < rows.length; i++) {
|
|
470
|
+
const row = rows[i];
|
|
471
|
+
const cells = row.content ?? [];
|
|
472
|
+
const cellTexts = cells.map((c) => {
|
|
473
|
+
const inner = (c.content ?? []).map(tiptapNodeToMarkdown).join("");
|
|
474
|
+
return inner.replace(/\n+$/, "").replace(/\n/g, "<br>").replace(/\|/g, "\\|");
|
|
475
|
+
});
|
|
476
|
+
renderedRows.push(cellTexts);
|
|
477
|
+
if (headerIdx === -1 && cells.some((c) => c.type === "tableHeader")) headerIdx = i;
|
|
478
|
+
}
|
|
479
|
+
if (headerIdx === -1) headerIdx = 0;
|
|
480
|
+
const header = renderedRows[headerIdx] ?? [];
|
|
481
|
+
const body = renderedRows.filter((_, i) => i !== headerIdx);
|
|
482
|
+
const cols = header.length;
|
|
483
|
+
const headerLine = "| " + header.join(" | ") + " |";
|
|
484
|
+
const sepLine = "| " + Array.from({ length: cols }, () => "---").join(" | ") + " |";
|
|
485
|
+
const bodyLines = body.map((r) => {
|
|
486
|
+
const cells = Array.from({ length: cols }, (_, i) => r[i] ?? "");
|
|
487
|
+
return "| " + cells.join(" | ") + " |";
|
|
488
|
+
});
|
|
489
|
+
return "\n" + [headerLine, sepLine, ...bodyLines].join("\n") + "\n\n";
|
|
490
|
+
}
|
|
470
491
|
function htmlToMarkdown(html) {
|
|
471
492
|
let md = html;
|
|
493
|
+
md = md.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, inner) => {
|
|
494
|
+
return "\n" + convertHtmlTable(String(inner)) + "\n";
|
|
495
|
+
});
|
|
472
496
|
md = md.replace(/<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi, (_, level, text) => {
|
|
473
497
|
return "\n" + "#".repeat(Number(level)) + " " + String(text).trim() + "\n\n";
|
|
474
498
|
});
|
|
@@ -481,6 +505,9 @@ function htmlToMarkdown(html) {
|
|
|
481
505
|
md = md.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, (_, content) => {
|
|
482
506
|
return "\n" + String(content).trim().split("\n").map((l) => "> " + l).join("\n") + "\n\n";
|
|
483
507
|
});
|
|
508
|
+
md = md.replace(/<ul[^>]*data-type="taskList"[^>]*>([\s\S]*?)<\/ul>/gi, (_, items) => {
|
|
509
|
+
return "\n" + convertHtmlTaskList(String(items)) + "\n";
|
|
510
|
+
});
|
|
484
511
|
md = md.replace(/<ul[^>]*>([\s\S]*?)<\/ul>/gi, (_, items) => {
|
|
485
512
|
return "\n" + String(items).replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "- $1\n") + "\n";
|
|
486
513
|
});
|
|
@@ -500,11 +527,63 @@ function htmlToMarkdown(html) {
|
|
|
500
527
|
md = md.replace(/<(em|i)>([\s\S]*?)<\/\1>/gi, "*$2*");
|
|
501
528
|
md = md.replace(/<s>([\s\S]*?)<\/s>/gi, "~~$1~~");
|
|
502
529
|
md = md.replace(/<code>([\s\S]*?)<\/code>/gi, "`$1`");
|
|
530
|
+
const PH_U_OPEN = "AMP_U_OPEN";
|
|
531
|
+
const PH_U_CLOSE = "AMP_U_CLOSE";
|
|
532
|
+
const PH_MARK_OPEN = "AMP_MARK_OPEN";
|
|
533
|
+
const PH_MARK_CLOSE = "AMP_MARK_CLOSE";
|
|
534
|
+
md = md.replace(/<u>([\s\S]*?)<\/u>/gi, `${PH_U_OPEN}$1${PH_U_CLOSE}`);
|
|
535
|
+
md = md.replace(/<mark>([\s\S]*?)<\/mark>/gi, `${PH_MARK_OPEN}$1${PH_MARK_CLOSE}`);
|
|
503
536
|
md = md.replace(/<\/?[^>]+>/g, "");
|
|
537
|
+
md = md.split(PH_U_OPEN).join("<u>").split(PH_U_CLOSE).join("</u>").split(PH_MARK_OPEN).join("<mark>").split(PH_MARK_CLOSE).join("</mark>");
|
|
504
538
|
md = md.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ");
|
|
505
539
|
md = md.replace(/\n{3,}/g, "\n\n");
|
|
506
540
|
return md.trim() + "\n";
|
|
507
541
|
}
|
|
542
|
+
function convertHtmlTable(inner) {
|
|
543
|
+
const stripped = inner.replace(/<\/?(thead|tbody)[^>]*>/gi, "");
|
|
544
|
+
const rowRe = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
|
|
545
|
+
const rows = [];
|
|
546
|
+
let m;
|
|
547
|
+
while ((m = rowRe.exec(stripped)) !== null) {
|
|
548
|
+
const rowHtml = m[1] ?? "";
|
|
549
|
+
const cellRe = /<(th|td)[^>]*>([\s\S]*?)<\/\1>/gi;
|
|
550
|
+
const cells = [];
|
|
551
|
+
let isHeader = false;
|
|
552
|
+
let cm;
|
|
553
|
+
while ((cm = cellRe.exec(rowHtml)) !== null) {
|
|
554
|
+
if ((cm[1] ?? "").toLowerCase() === "th") isHeader = true;
|
|
555
|
+
cells.push(normalizeTableCell(cm[2] ?? ""));
|
|
556
|
+
}
|
|
557
|
+
if (cells.length > 0) rows.push({ isHeader, cells });
|
|
558
|
+
}
|
|
559
|
+
if (rows.length === 0) return "";
|
|
560
|
+
let headerIdx = rows.findIndex((r) => r.isHeader);
|
|
561
|
+
if (headerIdx === -1) headerIdx = 0;
|
|
562
|
+
const header = rows[headerIdx].cells;
|
|
563
|
+
const body = rows.filter((_, i) => i !== headerIdx).map((r) => r.cells);
|
|
564
|
+
const cols = header.length;
|
|
565
|
+
const headerLine = "| " + header.join(" | ") + " |";
|
|
566
|
+
const sepLine = "| " + Array.from({ length: cols }, () => "---").join(" | ") + " |";
|
|
567
|
+
const bodyLines = body.map((cells) => {
|
|
568
|
+
const padded = Array.from({ length: cols }, (_, i) => cells[i] ?? "");
|
|
569
|
+
return "| " + padded.join(" | ") + " |";
|
|
570
|
+
});
|
|
571
|
+
return [headerLine, sepLine, ...bodyLines].join("\n") + "\n";
|
|
572
|
+
}
|
|
573
|
+
function normalizeTableCell(html) {
|
|
574
|
+
return html.replace(/<br\s*\/?>/gi, " ").replace(/<\/?[^>]+>/g, "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
575
|
+
}
|
|
576
|
+
function convertHtmlTaskList(items) {
|
|
577
|
+
const liRe = /<li[^>]*data-checked="(true|false)"[^>]*>([\s\S]*?)<\/li>/gi;
|
|
578
|
+
const out = [];
|
|
579
|
+
let m;
|
|
580
|
+
while ((m = liRe.exec(items)) !== null) {
|
|
581
|
+
const checked = m[1] === "true" ? "x" : " ";
|
|
582
|
+
const inner = String(m[2] ?? "").replace(/<\/?p[^>]*>/gi, "").trim();
|
|
583
|
+
out.push(`- [${checked}] ${inner}`);
|
|
584
|
+
}
|
|
585
|
+
return out.join("\n");
|
|
586
|
+
}
|
|
508
587
|
|
|
509
588
|
// src/index.ts
|
|
510
589
|
function createAmpless(opts) {
|
|
@@ -517,13 +596,13 @@ function createAmpless(opts) {
|
|
|
517
596
|
const themeConfig = createThemeConfig(themeActive, storage);
|
|
518
597
|
return {
|
|
519
598
|
listPublishedPosts: (o) => posts.listPublishedPosts(o),
|
|
520
|
-
getPublishedPost: (slug
|
|
599
|
+
getPublishedPost: (slug) => posts.getPublishedPost(slug),
|
|
521
600
|
listPostsByTag: (tag, o) => posts.listPostsByTag(tag, o),
|
|
522
|
-
loadSiteSettings: (
|
|
523
|
-
resolveActiveTheme: (
|
|
524
|
-
loadThemeConfig: (
|
|
525
|
-
postMetadata: (post
|
|
526
|
-
siteMetadata: (
|
|
601
|
+
loadSiteSettings: () => settings.loadSiteSettings(),
|
|
602
|
+
resolveActiveTheme: () => themeActive.resolveActiveTheme(),
|
|
603
|
+
loadThemeConfig: () => themeConfig.loadThemeConfig(),
|
|
604
|
+
postMetadata: (post) => seo.postMetadata(post),
|
|
605
|
+
siteMetadata: () => seo.siteMetadata(),
|
|
527
606
|
renderBody: (post) => renderBody(post),
|
|
528
607
|
renderThemeCss: (cssVars) => renderThemeCss(cssVars),
|
|
529
608
|
publicAssetUrl: (key) => storage.publicAssetUrl(key),
|
package/dist/middleware.d.ts
CHANGED
|
@@ -8,24 +8,26 @@ type MiddlewareFn = (request: NextRequest) => NextResponse;
|
|
|
8
8
|
/**
|
|
9
9
|
* Build the ampless public-site middleware. Performs:
|
|
10
10
|
*
|
|
11
|
-
* -
|
|
12
|
-
* - `/
|
|
11
|
+
* - `/path` → `/site/default/path` internal rewrite
|
|
12
|
+
* - `/_/<slug>(/...)` → `/site/default/r/<slug>(/...)` rewrite for
|
|
13
|
+
* the unified bare-HTML / static-bundle route
|
|
13
14
|
* - `?previewTheme=<name>` → `x-preview-theme` header forwarding
|
|
14
|
-
* - `Cache-Control: private, no-store` in multi-site mode (Amplify
|
|
15
|
-
* Hosting's CloudFront cache key doesn't include Host, so SSR
|
|
16
|
-
* responses would cross-contaminate at the same path)
|
|
17
15
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `/
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
16
|
+
* Bare-HTML / static routing is data-driven: the theme post dispatcher
|
|
17
|
+
* redirects `metadata.no_layout` and `format='static'` posts to
|
|
18
|
+
* `/_/<slug>`. The middleware rewrites the public `_` prefix to the
|
|
19
|
+
* routable internal folder name `r/`, because Next.js's App Router
|
|
20
|
+
* excludes any path part starting with `_` during route discovery
|
|
21
|
+
* (see `recursive-readdir` + `ignorePartFilter` in
|
|
22
|
+
* `next/dist/build/route-discovery.js`). The browser URL stays
|
|
23
|
+
* `/_/<slug>(/...)`; only the rewrite target uses `r/`.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* Single site per Amplify deployment: the rewritten internal path uses
|
|
26
|
+
* the fixed `default` segment. The internal `/site/[siteId]/` routing
|
|
27
|
+
* tree is retained as an implementation detail; a follow-up PR
|
|
28
|
+
* flattens the URL structure further.
|
|
27
29
|
*/
|
|
28
|
-
declare function createAmplessMiddleware(
|
|
30
|
+
declare function createAmplessMiddleware(_opts: CreateMiddlewareOpts): MiddlewareFn;
|
|
29
31
|
/**
|
|
30
32
|
* Reference matcher config — admin / api / login / static assets /
|
|
31
33
|
* amplify_outputs.json are excluded so middleware doesn't rewrite
|
package/dist/middleware.js
CHANGED
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
// src/middleware.ts
|
|
2
2
|
import { NextResponse } from "next/server";
|
|
3
|
-
|
|
4
|
-
function createAmplessMiddleware(
|
|
5
|
-
const MULTI_SITE = isMultiSite(cmsConfig);
|
|
3
|
+
var INTERNAL_SITE_SEGMENT = "default";
|
|
4
|
+
function createAmplessMiddleware(_opts) {
|
|
6
5
|
return function middleware(request) {
|
|
7
|
-
const host = (request.headers.get("host") ?? "").split(":")[0];
|
|
8
|
-
const siteId = resolveSiteId(host ?? "", cmsConfig);
|
|
9
|
-
if (!siteId) {
|
|
10
|
-
return new NextResponse("Site not found", { status: 404 });
|
|
11
|
-
}
|
|
12
6
|
const url = request.nextUrl.clone();
|
|
13
7
|
if (!url.pathname.startsWith("/site/")) {
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
let tail = url.pathname === "/" ? "" : url.pathname;
|
|
9
|
+
if (tail === "/_" || tail.startsWith("/_/")) {
|
|
10
|
+
tail = `/r${tail.slice(2)}`;
|
|
11
|
+
}
|
|
12
|
+
url.pathname = `/site/${INTERNAL_SITE_SEGMENT}${tail}`;
|
|
16
13
|
}
|
|
17
14
|
const previewTheme = url.searchParams.get("previewTheme");
|
|
18
15
|
const previewColorScheme = url.searchParams.get("previewColorScheme");
|
|
19
16
|
const requestHeaders = new Headers(request.headers);
|
|
20
|
-
requestHeaders.set("x-site-id", siteId);
|
|
21
17
|
if (previewTheme) requestHeaders.set("x-preview-theme", previewTheme);
|
|
22
18
|
if (previewColorScheme) requestHeaders.set("x-preview-color-scheme", previewColorScheme);
|
|
23
|
-
|
|
24
|
-
response.headers.set("x-site-id", siteId);
|
|
25
|
-
if (MULTI_SITE) {
|
|
26
|
-
response.headers.set("Cache-Control", "private, no-store");
|
|
27
|
-
}
|
|
28
|
-
return response;
|
|
19
|
+
return NextResponse.rewrite(url, { request: { headers: requestHeaders } });
|
|
29
20
|
};
|
|
30
21
|
}
|
|
31
22
|
var defaultMatcherConfig = {
|