diff --git a/package.json b/package.json
index de79fcb..783936e 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"fast-glob": "^3.3.2",
"lucide-react": "^0.396.0",
"markdown-it": "^14.1.0",
+ "node-html-parser": "^6.1.13",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-markdown": "^9.0.1",
@@ -41,6 +42,8 @@
"typescript": "^5.5.2"
},
"devDependencies": {
+ "@types/markdown-it": "^14.1.1",
+ "@types/sanitize-html": "^2.11.0",
"pocketbase": "^0.21.3"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 30a7b95..57f8466 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -62,6 +62,9 @@ importers:
markdown-it:
specifier: ^14.1.0
version: 14.1.0
+ node-html-parser:
+ specifier: ^6.1.13
+ version: 6.1.13
react:
specifier: ^18.3.1
version: 18.3.1
@@ -96,6 +99,12 @@ importers:
specifier: ^5.5.2
version: 5.5.2
devDependencies:
+ '@types/markdown-it':
+ specifier: ^14.1.1
+ version: 14.1.1
+ '@types/sanitize-html':
+ specifier: ^2.11.0
+ version: 2.11.0
pocketbase:
specifier: ^0.21.3
version: 0.21.3
@@ -932,9 +941,18 @@ packages:
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ '@types/linkify-it@5.0.0':
+ resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
+
+ '@types/markdown-it@14.1.1':
+ resolution: {integrity: sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==}
+
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+ '@types/mdurl@2.0.0':
+ resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
+
'@types/mdx@2.0.13':
resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
@@ -956,6 +974,9 @@ packages:
'@types/react@18.3.3':
resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==}
+ '@types/sanitize-html@2.11.0':
+ resolution: {integrity: sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==}
+
'@types/unist@2.0.10':
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
@@ -1139,6 +1160,9 @@ packages:
bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
boxen@7.1.1:
resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==}
engines: {node: '>=14.16'}
@@ -1285,6 +1309,13 @@ packages:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
+ css-select@5.1.0:
+ resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
+
+ css-what@6.1.0:
+ resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
+ engines: {node: '>= 6'}
+
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@@ -1610,6 +1641,10 @@ packages:
hastscript@8.0.0:
resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
+ he@1.2.0:
+ resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+ hasBin: true
+
html-escaper@3.0.3:
resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
@@ -2106,6 +2141,9 @@ packages:
resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==}
hasBin: true
+ node-html-parser@6.1.13:
+ resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==}
+
node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
@@ -2130,6 +2168,9 @@ packages:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
deprecated: This package is no longer supported.
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -3870,10 +3911,19 @@ snapshots:
dependencies:
'@types/unist': 3.0.2
+ '@types/linkify-it@5.0.0': {}
+
+ '@types/markdown-it@14.1.1':
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.2
+ '@types/mdurl@2.0.0': {}
+
'@types/mdx@2.0.13': {}
'@types/ms@0.7.34': {}
@@ -3897,6 +3947,10 @@ snapshots:
'@types/prop-types': 15.7.12
csstype: 3.1.3
+ '@types/sanitize-html@2.11.0':
+ dependencies:
+ htmlparser2: 8.0.2
+
'@types/unist@2.0.10': {}
'@types/unist@3.0.2': {}
@@ -4176,6 +4230,8 @@ snapshots:
dependencies:
file-uri-to-path: 1.0.0
+ boolbase@1.0.0: {}
+
boxen@7.1.1:
dependencies:
ansi-align: 3.0.1
@@ -4315,6 +4371,16 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ css-select@5.1.0:
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 6.1.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ nth-check: 2.1.1
+
+ css-what@6.1.0: {}
+
cssesc@3.0.0: {}
csstype@3.1.3: {}
@@ -4741,6 +4807,8 @@ snapshots:
property-information: 6.5.0
space-separated-tokens: 2.0.2
+ he@1.2.0: {}
+
html-escaper@3.0.3: {}
html-url-attributes@3.0.0: {}
@@ -5448,6 +5516,11 @@ snapshots:
node-gyp-build@4.8.1: {}
+ node-html-parser@6.1.13:
+ dependencies:
+ css-select: 5.1.0
+ he: 1.2.0
+
node-releases@2.0.14: {}
nopt@5.0.0:
@@ -5469,6 +5542,10 @@ snapshots:
gauge: 3.0.2
set-blocking: 2.0.0
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
object-assign@4.1.1: {}
object-hash@3.0.0: {}
diff --git a/src/content/blog/rewind-2023-and-future-plans.mdx b/src/content/blog/rewind-2023-and-future-plans.mdx
index 380e2b4..d1069ad 100644
--- a/src/content/blog/rewind-2023-and-future-plans.mdx
+++ b/src/content/blog/rewind-2023-and-future-plans.mdx
@@ -106,4 +106,4 @@ overwhelming myself, I'll keep my **ambitions simple**, but **constant**.
### Happy holidays!
-
+
diff --git a/src/pages/rss.xml.js b/src/pages/rss.xml.js
deleted file mode 100644
index 0a0cefb..0000000
--- a/src/pages/rss.xml.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import rss from "@astrojs/rss";
-import { getCollection } from "astro:content";
-import sanitizeHtml from "sanitize-html";
-import MarkdownIt from "markdown-it";
-const parser = new MarkdownIt();
-
-export async function GET(context) {
- const blog = await getCollection(
- "blog",
- ({ data }) => data.draft !== true && data.rss === true,
- );
- const portfolio = await getCollection(
- "portfolio",
- ({ data }) => data.draft !== true && data.rss === true,
- );
-
- const blogItems = blog.map((post) => ({
- title: post.data.title,
- pubDate: post.data.date,
- description: post.data.description,
- tags: post.data.tags,
- author: post.data.author,
- link: `/blog/${post.slug}/`,
- content: sanitizeHtml(parser.render(post.body), {
- allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
- }),
- }));
-
- const portfolioItems = portfolio.map((project) => ({
- title: project.data.title,
- pubDate: project.data.date,
- description: project.data.description,
- tags: project.data.tags,
- author: project.data.author,
- link: `/portfolio/${project.slug}/`,
- content: sanitizeHtml(parser.render(project.body), {
- allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
- }),
- }));
-
- const items = [...blogItems, ...portfolioItems];
-
- return rss({
- title: "juancmandev",
- description: "Welcome to my domain, stranger.",
- customData: `en-us${new Date()}`,
- site: context.site,
- items,
- });
-}
diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts
new file mode 100644
index 0000000..df99679
--- /dev/null
+++ b/src/pages/rss.xml.ts
@@ -0,0 +1,125 @@
+import rss from "@astrojs/rss";
+import type { RSSFeedItem } from "@astrojs/rss";
+import { getCollection } from "astro:content";
+import sanitizeHtml from "sanitize-html";
+import MarkdownIt from "markdown-it";
+import { parse as htmlParser } from "node-html-parser";
+import { getImage } from "astro:assets";
+import type { ImageMetadata } from "astro";
+
+const markdownParser = new MarkdownIt();
+
+const imagesBlog = import.meta.glob<{ default: ImageMetadata }>(
+ "/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}",
+);
+const imagesPortfolio = import.meta.glob<{ default: ImageMetadata }>(
+ "/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}",
+);
+
+export async function GET(context: any) {
+ const items: RSSFeedItem[] = [];
+
+ const blog = await getCollection(
+ "blog",
+ ({ data }) => data.draft !== true && data.rss === true,
+ );
+ const portfolio = await getCollection(
+ "portfolio",
+ ({ data }) => data.draft !== true && data.rss === true,
+ );
+
+ for await (const post of blog) {
+ const body = markdownParser.render(post.body);
+ const html = htmlParser.parse(body);
+ const images = html.querySelectorAll("img");
+
+ for await (const img of images) {
+ const src = img.getAttribute("src")!;
+ console.log(src);
+
+ if (src.startsWith("@/")) {
+ const prefixRemoved = src.replace("@/", "");
+ const imagePathPrefix = `/src/${prefixRemoved}`;
+ const imagePath = await imagesBlog[imagePathPrefix]?.()?.then(
+ (res: any) => res.default,
+ );
+
+ if (imagePath) {
+ const optimizedImg = await getImage({ src: imagePath });
+ img.setAttribute(
+ "src",
+ context.site + optimizedImg.src.replace("/", ""),
+ );
+ }
+ } else if (src.startsWith("/images")) {
+ img.setAttribute("src", context.site + src.replace("/", ""));
+ } else {
+ throw Error("src unknown");
+ }
+ }
+
+ items.push({
+ title: post.data.title,
+ pubDate: post.data.date,
+ description: post.data.description,
+ author: post.data.author,
+ link: `/blog/${post.slug}/`,
+ content: sanitizeHtml(html.toString(), {
+ allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
+ }),
+ });
+ }
+
+ for await (const project of portfolio) {
+ const body = markdownParser.render(project.body);
+ const html = htmlParser.parse(body);
+ const images = html.querySelectorAll("img");
+
+ for await (const img of images) {
+ const src = img.getAttribute("src")!;
+
+ if (src.startsWith("@/")) {
+ const prefixRemoved = src.replace("@/", "");
+ const imagePathPrefix = `/src/${prefixRemoved}`;
+ const imagePath = await imagesPortfolio[imagePathPrefix]?.()?.then(
+ (res: any) => res.default,
+ );
+
+ if (imagePath) {
+ const optimizedImg = await getImage({ src: imagePath });
+ img.setAttribute(
+ "src",
+ context.site + optimizedImg.src.replace("/", ""),
+ );
+ }
+ } else if (src.startsWith("/images")) {
+ // images starting with `/images/` is the public dir
+ img.setAttribute("src", context.site + src.replace("/", ""));
+ } else {
+ throw Error("src unknown");
+ }
+ }
+
+ items.push({
+ title: project.data.title,
+ pubDate: project.data.date,
+ description: project.data.description,
+ author: project.data.author,
+ link: `/portfolio/${project.slug}/`,
+ content: sanitizeHtml(html.toString(), {
+ allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
+ }),
+ });
+ }
+
+ console.log(items);
+
+ return rss({
+ title: "juancmandev",
+ description: "Welcome to my domain, stranger.",
+ customData: `en-us`,
+ site: context.site,
+ items,
+ trailingSlash: false,
+ });
+}