chore: update comments-locales patch for 1.0.10 template, drop livefetch patch

patch-endpoint-comments-locales: remove viewReplacements and sourceOverrides
since comments.njk was rewritten with Nunjucks macros and badge() — the old
HTML snippets no longer exist. Also drop obsolete locale keys (hiddenBadge,
targetPrefix, paginationLabel, page, of) that the new template doesn't use.

patch-webmention-sender-livefetch: delete — was orphaned (not in postinstall)
and the behavior is intentionally accepted as-is.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sven
2026-03-14 23:12:57 +01:00
parent 53bb7d363b
commit 53b40a5edc
2 changed files with 4 additions and 268 deletions

View File

@@ -29,20 +29,6 @@ const localeAliases = {
"pt-BR": "pt", "pt-BR": "pt",
}; };
const sourceOverrides = {
comments: {
dashboard: {
hiddenBadge: "Hidden",
targetPrefix: "on:",
paginationLabel: "Pagination",
previous: "Previous",
page: "Page",
of: "of",
next: "Next",
},
},
};
const localeOverrides = { const localeOverrides = {
de: { de: {
comments: { comments: {
@@ -67,12 +53,7 @@ const localeOverrides = {
commentHidden: "Kommentar versteckt", commentHidden: "Kommentar versteckt",
commentPurged: "Kommentar geloescht", commentPurged: "Kommentar geloescht",
commentRestored: "Kommentar wiederhergestellt", commentRestored: "Kommentar wiederhergestellt",
hiddenBadge: "Versteckt",
targetPrefix: "zu:",
paginationLabel: "Seitennavigation",
previous: "Zurueck", previous: "Zurueck",
page: "Seite",
of: "von",
next: "Weiter", next: "Weiter",
}, },
api: { api: {
@@ -107,12 +88,7 @@ const localeOverrides = {
commentHidden: "Comentario ocultado", commentHidden: "Comentario ocultado",
commentPurged: "Comentario eliminado", commentPurged: "Comentario eliminado",
commentRestored: "Comentario restaurado", commentRestored: "Comentario restaurado",
hiddenBadge: "Oculto",
targetPrefix: "en:",
paginationLabel: "Paginacion",
previous: "Anterior", previous: "Anterior",
page: "Pagina",
of: "de",
next: "Siguiente", next: "Siguiente",
}, },
api: { api: {
@@ -147,12 +123,7 @@ const localeOverrides = {
commentHidden: "Commentaire masque", commentHidden: "Commentaire masque",
commentPurged: "Commentaire supprime", commentPurged: "Commentaire supprime",
commentRestored: "Commentaire restaure", commentRestored: "Commentaire restaure",
hiddenBadge: "Masque",
targetPrefix: "sur:",
paginationLabel: "Pagination",
previous: "Precedent", previous: "Precedent",
page: "Page",
of: "de",
next: "Suivant", next: "Suivant",
}, },
api: { api: {
@@ -187,12 +158,7 @@ const localeOverrides = {
commentHidden: "Reactie verborgen", commentHidden: "Reactie verborgen",
commentPurged: "Reactie verwijderd", commentPurged: "Reactie verwijderd",
commentRestored: "Reactie hersteld", commentRestored: "Reactie hersteld",
hiddenBadge: "Verborgen",
targetPrefix: "op:",
paginationLabel: "Paginatie",
previous: "Vorige", previous: "Vorige",
page: "Pagina",
of: "van",
next: "Volgende", next: "Volgende",
}, },
api: { api: {
@@ -227,12 +193,7 @@ const localeOverrides = {
commentHidden: "Comentario ocultado", commentHidden: "Comentario ocultado",
commentPurged: "Comentario removido", commentPurged: "Comentario removido",
commentRestored: "Comentario restaurado", commentRestored: "Comentario restaurado",
hiddenBadge: "Oculto",
targetPrefix: "em:",
paginationLabel: "Paginacao",
previous: "Anterior", previous: "Anterior",
page: "Pagina",
of: "de",
next: "Proximo", next: "Proximo",
}, },
api: { api: {
@@ -267,12 +228,7 @@ const localeOverrides = {
commentHidden: "Kommentar dold", commentHidden: "Kommentar dold",
commentPurged: "Kommentar raderad", commentPurged: "Kommentar raderad",
commentRestored: "Kommentar aterstalld", commentRestored: "Kommentar aterstalld",
hiddenBadge: "Dold",
targetPrefix: "pa:",
paginationLabel: "Sidindelning",
previous: "Foregaende", previous: "Foregaende",
page: "Sida",
of: "av",
next: "Nasta", next: "Nasta",
}, },
api: { api: {
@@ -286,43 +242,6 @@ const localeOverrides = {
}, },
}; };
const viewReplacements = [
{
oldSnippet: '<span class="badge badge--red">Hidden</span>',
newSnippet:
'<span class="badge badge--red">{{ __("comments.dashboard.hiddenBadge") if __ else "Hidden" }}</span>',
},
{
oldSnippet: " on: {{ comment.target }}",
newSnippet:
' {{ __("comments.dashboard.targetPrefix") if __ else "on:" }} {{ comment.target }}',
},
{
oldSnippet: ` {% if totalPages > 1 %}
<nav class="flex gap-2 justify-center mt-6" aria-label="Pagination">
{% if page > 1 %}
<a href="{{ baseUrl }}?page={{ page - 1 }}&status={{ statusFilter }}" class="button button--secondary button--small">Previous</a>
{% endif %}
<span class="text-sm self-center">Page {{ page }} of {{ totalPages }}</span>
{% if page < totalPages %}
<a href="{{ baseUrl }}?page={{ page + 1 }}&status={{ statusFilter }}" class="button button--secondary button--small">Next</a>
{% endif %}
</nav>
{% endif %}`,
newSnippet: ` {% if totalPages > 1 %}
<nav class="flex gap-2 justify-center mt-6" aria-label="{{ __("comments.dashboard.paginationLabel") if __ else "Pagination" }}">
{% if page > 1 %}
<a href="{{ baseUrl }}?page={{ page - 1 }}&status={{ statusFilter }}" class="button button--secondary button--small">{{ __("comments.dashboard.previous") if __ else "Previous" }}</a>
{% endif %}
<span class="text-sm self-center">{{ __("comments.dashboard.page") if __ else "Page" }} {{ page }} {{ __("comments.dashboard.of") if __ else "of" }} {{ totalPages }}</span>
{% if page < totalPages %}
<a href="{{ baseUrl }}?page={{ page + 1 }}&status={{ statusFilter }}" class="button button--secondary button--small">{{ __("comments.dashboard.next") if __ else "Next" }}</a>
{% endif %}
</nav>
{% endif %}`,
},
];
function isObject(value) { function isObject(value) {
return Boolean(value) && typeof value === "object" && !Array.isArray(value); return Boolean(value) && typeof value === "object" && !Array.isArray(value);
} }
@@ -377,8 +296,6 @@ async function exists(filePath) {
let checkedEndpoints = 0; let checkedEndpoints = 0;
let checkedLocales = 0; let checkedLocales = 0;
let patchedLocales = 0; let patchedLocales = 0;
let checkedTemplates = 0;
let patchedTemplates = 0;
for (const endpointPath of endpointCandidates) { for (const endpointPath of endpointCandidates) {
if (!(await exists(endpointPath))) { if (!(await exists(endpointPath))) {
@@ -400,14 +317,8 @@ for (const endpointPath of endpointCandidates) {
continue; continue;
} }
const sourcePatched = applyOverrides(sourceLocaleJson, sourceOverrides);
checkedLocales += 1; checkedLocales += 1;
if (JSON.stringify(sourcePatched) !== JSON.stringify(sourceLocaleJson)) {
await writeFile(sourcePath, `${JSON.stringify(sourcePatched, null, 2)}\n`, "utf8");
patchedLocales += 1;
}
for (const locale of targetLocales) { for (const locale of targetLocales) {
const localePath = path.join(endpointPath, "locales", `${locale}.json`); const localePath = path.join(endpointPath, "locales", `${locale}.json`);
checkedLocales += 1; checkedLocales += 1;
@@ -421,7 +332,7 @@ for (const endpointPath of endpointCandidates) {
} }
} }
const merged = mergeMissing(localeJson, sourcePatched); const merged = mergeMissing(localeJson, sourceLocaleJson);
const overrideKey = localeAliases[locale] || locale; const overrideKey = localeAliases[locale] || locale;
const patched = applyOverrides(merged, localeOverrides[overrideKey] || {}); const patched = applyOverrides(merged, localeOverrides[overrideKey] || {});
@@ -432,45 +343,14 @@ for (const endpointPath of endpointCandidates) {
await writeFile(localePath, `${JSON.stringify(patched, null, 2)}\n`, "utf8"); await writeFile(localePath, `${JSON.stringify(patched, null, 2)}\n`, "utf8");
patchedLocales += 1; patchedLocales += 1;
} }
const viewPath = path.join(endpointPath, "views", "comments.njk");
if (!(await exists(viewPath))) {
continue;
}
checkedTemplates += 1;
const viewSource = await readFile(viewPath, "utf8");
let viewUpdated = viewSource;
let templateChanged = false;
for (const replacement of viewReplacements) {
if (viewUpdated.includes(replacement.newSnippet)) {
continue;
}
if (!viewUpdated.includes(replacement.oldSnippet)) {
continue;
}
viewUpdated = viewUpdated.replace(replacement.oldSnippet, replacement.newSnippet);
templateChanged = true;
}
if (!templateChanged) {
continue;
}
await writeFile(viewPath, viewUpdated, "utf8");
patchedTemplates += 1;
} }
if (checkedEndpoints === 0) { if (checkedEndpoints === 0) {
console.log("[postinstall] No comments endpoint directories found"); console.log("[postinstall] No comments endpoint directories found");
} else if (patchedLocales === 0 && patchedTemplates === 0) { } else if (patchedLocales === 0) {
console.log("[postinstall] comments locales and templates already patched"); console.log("[postinstall] comments locales already patched");
} else { } else {
console.log( console.log(
`[postinstall] Patched comments locales in ${patchedLocales}/${checkedLocales} file(s) and templates in ${patchedTemplates}/${checkedTemplates} file(s)`, `[postinstall] Patched comments locales in ${patchedLocales}/${checkedLocales} file(s)`,
); );
} }

View File

@@ -1,144 +0,0 @@
/**
* Patch @rmdes/indiekit-endpoint-webmention-sender controller to:
*
* 1. Always fetch the live page instead of using stored post content.
* The stored content (post.properties.content.html) is just the post body —
* it never contains template-rendered links like u-in-reply-to, u-like-of,
* u-bookmark-of, u-repost-of. Only the live HTML has those.
*
* 2. Don't permanently mark a post as webmention-sent when the live page
* is unreachable (e.g. deploy still in progress). Skip it silently so
* the next poll retries it.
*
* Handles both the original upstream code and the older patch-webmention-sender-retry
* variant (which only fixed the no-content case but not the live-fetch case).
*/
import { access, readFile, writeFile } from "node:fs/promises";
const filePath =
"node_modules/@rmdes/indiekit-endpoint-webmention-sender/lib/controllers/webmention-sender.js";
const patchMarker = "// [patched:livefetch]";
// Original upstream code
const originalBlock = ` // If no content, try fetching the published page
let contentToProcess = postContent;
if (!contentToProcess) {
try {
const pageResponse = await fetch(postUrl);
if (pageResponse.ok) {
contentToProcess = await pageResponse.text();
}
} catch (error) {
console.log(\`[webmention] Could not fetch \${postUrl}: \${error.message}\`);
}
}
if (!contentToProcess) {
console.log(\`[webmention] No content to process for \${postUrl}\`);
await markWebmentionsSent(postsCollection, postUrl, { sent: [], failed: [], skipped: [] });
continue;
}`;
// State left by older patch-webmention-sender-retry.mjs (which only fixed the
// fetch-failure path but not the live-fetch-always path)
const retryPatchedBlock = ` // If no content, try fetching the published page
let contentToProcess = postContent;
let fetchFailed = false;
if (!contentToProcess) {
try {
const pageResponse = await fetch(postUrl);
if (pageResponse.ok) {
contentToProcess = await pageResponse.text();
} else {
fetchFailed = true;
}
} catch (error) {
fetchFailed = true;
console.log(\`[webmention] Could not fetch \${postUrl}: \${error.message}\`);
}
}
if (!contentToProcess) {
if (fetchFailed) {
// Page not yet available — skip and retry on next poll rather than
// permanently marking this post as sent with zero webmentions.
console.log(\`[webmention] Page not yet available for \${postUrl}, will retry next poll\`);
continue;
}
console.log(\`[webmention] No content to process for \${postUrl}\`);
await markWebmentionsSent(postsCollection, postUrl, { sent: [], failed: [], skipped: [] });
continue;
}`;
const newBlock = ` // [patched:livefetch] Always fetch the live page so template-rendered links
// (u-in-reply-to, u-like-of, u-bookmark-of, u-repost-of, etc.) are included.
// Stored content only has the post body, not these microformat links.
let contentToProcess = "";
try {
const pageResponse = await fetch(postUrl);
if (pageResponse.ok) {
contentToProcess = await pageResponse.text();
} else {
console.log(\`[webmention] Live page returned \${pageResponse.status} for \${postUrl}\`);
}
} catch (error) {
console.log(\`[webmention] Could not fetch \${postUrl}: \${error.message}\`);
}
// Fall back to stored content if live page is unavailable
if (!contentToProcess) {
contentToProcess = postContent;
}
if (!contentToProcess) {
// Page not reachable yet (deploy in progress?) — skip without marking sent
// so the next poll retries it.
console.log(\`[webmention] No content available for \${postUrl}, will retry next poll\`);
continue;
}`;
async function exists(p) {
try {
await access(p);
return true;
} catch {
return false;
}
}
if (!(await exists(filePath))) {
console.log("[patch-webmention-sender-livefetch] File not found, skipping");
process.exit(0);
}
const source = await readFile(filePath, "utf8");
if (source.includes(patchMarker)) {
console.log("[patch-webmention-sender-livefetch] Already patched");
process.exit(0);
}
const targetBlock = source.includes(originalBlock)
? originalBlock
: source.includes(retryPatchedBlock)
? retryPatchedBlock
: null;
if (!targetBlock) {
console.warn(
"[patch-webmention-sender-livefetch] Target block not found — upstream format may have changed, skipping"
);
process.exit(0);
}
const patched = source.replace(targetBlock, newBlock);
if (!patched.includes(patchMarker)) {
console.warn("[patch-webmention-sender-livefetch] Patch validation failed, skipping");
process.exit(0);
}
await writeFile(filePath, patched, "utf8");
console.log("[patch-webmention-sender-livefetch] Patched successfully");