mirror of
https://github.com/svemagie/indiekit-endpoint-blogroll.git
synced 2026-04-02 15:34:59 +02:00
feat: category tabs, future post badge, HTML entity decoding
- Decode HTML entities (& ' etc) in feed titles and summaries - Add isFuture flag to API items for future-dated posts - Bump version to 1.0.12 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -249,12 +249,14 @@ function sanitizeBlog(blog) {
|
||||
* @returns {object} Sanitized item
|
||||
*/
|
||||
function sanitizeItem(item) {
|
||||
const published = item.published ? new Date(item.published) : null;
|
||||
return {
|
||||
id: item._id.toString(),
|
||||
url: item.url,
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
published: item.published,
|
||||
isFuture: published ? published > new Date() : false,
|
||||
author: item.author,
|
||||
photo: item.photo,
|
||||
categories: item.categories,
|
||||
|
||||
@@ -134,14 +134,14 @@ function parseJsonFeed(content, feedUrl, maxItems) {
|
||||
const items = (feed.items || []).slice(0, maxItems).map((item) => ({
|
||||
uid: generateUid(feedUrl, item.id || item.url),
|
||||
url: item.url || item.external_url,
|
||||
title: item.title || "Untitled",
|
||||
title: decodeEntities(item.title) || "Untitled",
|
||||
content: {
|
||||
html: item.content_html
|
||||
? sanitizeHtml(item.content_html, SANITIZE_OPTIONS)
|
||||
: undefined,
|
||||
text: item.content_text,
|
||||
},
|
||||
summary: item.summary || truncateText(item.content_text, 300),
|
||||
summary: decodeEntities(item.summary) || truncateText(item.content_text, 300),
|
||||
published: item.date_published ? new Date(item.date_published) : new Date(),
|
||||
updated: item.date_modified ? new Date(item.date_modified) : undefined,
|
||||
author: item.author || (item.authors?.[0]),
|
||||
@@ -171,7 +171,7 @@ function normalizeItem(item, feedUrl) {
|
||||
return {
|
||||
uid: generateUid(feedUrl, item.guid || item.link),
|
||||
url: item.link || item.origlink,
|
||||
title: item.title || "Untitled",
|
||||
title: decodeEntities(item.title) || "Untitled",
|
||||
content: {
|
||||
html: description ? sanitizeHtml(description, SANITIZE_OPTIONS) : undefined,
|
||||
text: stripHtml(description),
|
||||
@@ -200,16 +200,37 @@ function generateUid(feedUrl, itemId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip HTML tags from string
|
||||
* Strip HTML tags and decode HTML entities from string
|
||||
* @param {string} html - HTML string
|
||||
* @returns {string} Plain text
|
||||
*/
|
||||
function stripHtml(html) {
|
||||
if (!html) return "";
|
||||
return html
|
||||
return decodeEntities(
|
||||
html
|
||||
.replace(/<[^>]*>/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
.trim()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode HTML entities to their character equivalents
|
||||
* @param {string} str - String with HTML entities
|
||||
* @returns {string} Decoded string
|
||||
*/
|
||||
function decodeEntities(str) {
|
||||
if (!str) return "";
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/'/g, "'")
|
||||
.replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code)))
|
||||
.replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@rmdes/indiekit-endpoint-blogroll",
|
||||
"version": "1.0.11",
|
||||
"version": "1.0.12",
|
||||
"description": "Blogroll endpoint for Indiekit. Aggregates blog feeds from OPML, JSON feeds, or manual entry.",
|
||||
"keywords": [
|
||||
"indiekit",
|
||||
|
||||
Reference in New Issue
Block a user