Personal Site
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Generate a better RSS feed and generate styles to go along with it.

The files had to be moved out of /rss bc having an index was breaking things. The `.xml` extension had to be added to stop firefox downloading it (???)
idk man this is a bit scuffed but noone uses RSS anyway so like who gives a fuck

+171 -39
+108
src/pages/rss.style.xml.ts
··· 1 + import BoxTlbr from "/assets/box-tlbr.png"; 2 + import Hr from "/assets/hr.png"; 3 + import Box2x1Mask from "/assets/box-2x1-mask.png"; 4 + 5 + export async function GET() { 6 + return new Response( 7 + ` 8 + <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 9 + <xsl:output method="html" /> 10 + 11 + <xsl:template match="/"> 12 + <html lang="en"> 13 + <head> 14 + <title>RSS | vielle.dev</title> 15 + <style> 16 + :root { 17 + font-family: sans-serif; 18 + interpolate-size: allow-keywords; 19 + image-rendering: pixelated; 20 + } 21 + 22 + * { 23 + margin: 0; 24 + padding: 0; 25 + } 26 + 27 + html, 28 + body { 29 + width: 30ch; 30 + margin: auto; 31 + } 32 + 33 + img, 34 + svg, 35 + iframe, 36 + audio, 37 + video { 38 + max-width: 100%; 39 + display: block; 40 + } 41 + 42 + .post { 43 + border-image: url("${BoxTlbr.src}") 10 10 fill / 20px 20px round; 44 + padding: 30px; 45 + } 46 + 47 + img { 48 + width: 100%; 49 + aspect-ratio: 2; 50 + object-fit: cover; 51 + mask-image: url("${Box2x1Mask.src}"); 52 + mask-size: 100%; 53 + } 54 + 55 + a { 56 + color: black; 57 + text-decoration-line: none; 58 + } 59 + 60 + a:hover, 61 + a:focus, 62 + a:active { 63 + text-decoration-line: underline; 64 + } 65 + 66 + a:active { 67 + text-decoration-style: dashed; 68 + } 69 + </style> 70 + </head> 71 + 72 + <body> 73 + <section class="blog"> 74 + <h1>Blog Posts</h1> 75 + <p>If you want to use this page in your RSS reader, simply paste this page's URL in and it'll just work. To view the original RSS, view the page source. The styling of this page is made possible by <a href="https://developer.mozilla.org/en-US/docs/Web/XML/XSLT" target="_blank">XSLT</a></p> 76 + <xsl:for-each select="/rss/channel/item"> 77 + <a target="_blank"> 78 + <xsl:attribute name="href"> 79 + <xsl:value-of select="link" /> 80 + </xsl:attribute> 81 + <article class="post"> 82 + 83 + <img alt=""> 84 + <xsl:attribute name="src"> 85 + <xsl:value-of select="image" /> 86 + </xsl:attribute> 87 + </img> 88 + 89 + <h2 class="title"><xsl:value-of select="title" /></h2> 90 + <div class="bio"><xsl:value-of select="description" /></div> 91 + <time><xsl:value-of select="pubDate" /></time> 92 + 93 + </article> 94 + </a> 95 + </xsl:for-each> 96 + </section> 97 + </body> 98 + </html> 99 + </xsl:template> 100 + </xsl:stylesheet> 101 + `, 102 + { 103 + headers: { 104 + "Content-Type": "text/xml", 105 + }, 106 + }, 107 + ); 108 + }
+63
src/pages/rss.xml.ts
··· 1 + import { getCollection } from "astro:content"; 2 + import { getRssString } from "@astrojs/rss"; 3 + import type { APIRoute } from "astro"; 4 + import { experimental_AstroContainer } from "astro/container"; 5 + 6 + import image from "/content/blog/assets/nested.png"; 7 + image.format; 8 + 9 + const container = await experimental_AstroContainer.create(); 10 + 11 + const postBodies = import.meta.glob("../content/blog/**/*.md"); 12 + console.log("postBodies", postBodies); 13 + 14 + export const GET: APIRoute = async ({ site }) => { 15 + const posts = await getCollection("blog"); 16 + 17 + const rss = await getRssString({ 18 + title: "vielle.dev", 19 + description: "Posts from vielle.dev", 20 + site: site?.toString() ?? "", 21 + items: await Promise.all( 22 + posts.map(async (post) => { 23 + const res = { 24 + title: post.data.title, 25 + description: post.data.bio, 26 + pubDate: post.data.pub, 27 + link: `/blog/${post.id}/`, 28 + customData: await import( 29 + `../content/blog/assets/${post.data.banner.replace(".png", "")}.png` 30 + ).then( 31 + ({ default: img }: { default: ImageMetadata }) => 32 + // custom image component for the xslt transform 33 + `<image>${img.src}</image>`, 34 + ), 35 + }; 36 + 37 + if (post.filePath) { 38 + const { default: Content } = (await postBodies[ 39 + `../content/blog/${post.id}.md` 40 + ]()) as any; 41 + const content = await container.renderToString(Content); 42 + 43 + return { 44 + ...res, 45 + content: content, 46 + }; 47 + } else return res; 48 + }), 49 + ), 50 + }); 51 + 52 + return new Response( 53 + rss.replace( 54 + '<?xml version="1.0" encoding="UTF-8"?>', 55 + '<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss.style.xml" type="text/xsl" ?>', 56 + ), 57 + { 58 + headers: { 59 + "Content-Type": "application/xml", 60 + }, 61 + }, 62 + ); 63 + };
-39
src/pages/rss/index.ts
··· 1 - import { getCollection } from "astro:content"; 2 - import rss from "@astrojs/rss"; 3 - import type { APIRoute } from "astro"; 4 - import { experimental_AstroContainer } from "astro/container"; 5 - 6 - const container = await experimental_AstroContainer.create(); 7 - 8 - export const GET: APIRoute = async ({ site }) => { 9 - const posts = await getCollection("blog"); 10 - 11 - return rss({ 12 - title: "vielle.dev", 13 - description: "Posts from vielle.dev", 14 - site: site?.toString() ?? "", 15 - items: await Promise.all( 16 - posts.map(async (post) => { 17 - const res = { 18 - title: post.data.title, 19 - description: post.data.bio, 20 - pubDate: post.data.pub, 21 - link: `/blog/${post.id}/`, 22 - }; 23 - 24 - if (post.filePath) { 25 - /* its ssg so dynamic imports are fine. 26 - ../../ is bc it includes src/components 27 - so we need to go to project root */ 28 - const { default: Component } = await import( 29 - /* @vite-ignore */ `../../../${post.filePath}` 30 - ); 31 - const content = await container.renderToString(Component); 32 - 33 - return { ...res, content: content }; 34 - } else return res; 35 - }), 36 - ), 37 - // stylesheet: ``, 38 - }); 39 - };
src/pages/rss/style.ts

This is a binary file and will not be displayed.