From b40f7a7f531b4605f48cbaa6f173419826dca936 Mon Sep 17 00:00:00 2001 From: J <80164315+jbolns@users.noreply.github.com> Date: Fri, 25 Jul 2025 14:06:36 +0300 Subject: [PATCH 1/5] RSS feed & Sitemap Prelim for Review --- astro.config.mjs | 8 ++++- package.json | 1 + pnpm-lock.yaml | 46 ++++++++++++++++++++++++++++ src/layouts/Layout.astro | 11 ++++++- src/pages/rss.xml.ts | 65 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/pages/rss.xml.ts diff --git a/astro.config.mjs b/astro.config.mjs index 2d88e9677..69f844d00 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -15,6 +15,8 @@ import { } from './src/remark/directives'; import remarkOnlyStrong from './src/remark/only-strong'; import svgr from 'vite-plugin-svgr'; +import sitemap from '@astrojs/sitemap'; + // https://astro.build/config export default defineConfig({ @@ -37,6 +39,10 @@ export default defineConfig({ mdx(), react(), dotHtmlRedirects(), + sitemap({ + filter: (page) => page !== 'https://unitary.foundation/test/' && + page !== 'https://unitary.foundation/author/image/', + }), ], markdown: { remarkPlugins: [ @@ -63,4 +69,4 @@ export default defineConfig({ }), ], }, -}); +}); \ No newline at end of file diff --git a/package.json b/package.json index f774f75eb..c1d870abf 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@astrojs/mdx": "^4.3.0", "@astrojs/react": "4.3.0", "@astrojs/rss": "^4.0.12", + "@astrojs/sitemap": "^3.4.1", "@astrojs/tailwind": "6.0.2", "@cloudinary/url-gen": "^1.21.0", "@emotion/css": "^11.13.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f92479b2d..8f310c861 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@astrojs/rss': specifier: ^4.0.12 version: 4.0.12 + '@astrojs/sitemap': + specifier: ^3.4.1 + version: 3.4.1 '@astrojs/tailwind': specifier: 6.0.2 version: 6.0.2(astro@5.9.4(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(rollup@4.40.0)(sass@1.86.3)(typescript@5.8.3)(yaml@2.7.1))(tailwindcss@3.4.17) @@ -255,6 +258,9 @@ packages: '@astrojs/rss@4.0.12': resolution: {integrity: sha512-O5yyxHuDVb6DQ6VLOrbUVFSm+NpObulPxjs6XT9q3tC+RoKbN4HXMZLpv0LvXd1qdAjzVgJ1NFD+zKHJNDXikw==} + '@astrojs/sitemap@3.4.1': + resolution: {integrity: sha512-VjZvr1e4FH6NHyyHXOiQgLiw94LnCVY4v06wN/D0gZKchTMkg71GrAHJz81/huafcmavtLkIv26HnpfDq6/h/Q==} + '@astrojs/tailwind@6.0.2': resolution: {integrity: sha512-j3mhLNeugZq6A8dMNXVarUa8K6X9AW+QHU9u3lKNrPLMHhOQ0S7VeWhHwEeJFpEK1BTKEUY1U78VQv2gN6hNGg==} peerDependencies: @@ -1507,6 +1513,9 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@22.14.1': resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} @@ -1527,6 +1536,9 @@ packages: '@types/sanitize-html@2.15.0': resolution: {integrity: sha512-71Z6PbYsVKfp4i6Jvr37s5ql6if1Q/iJQT80NbaSi7uGaG8CqBMXP0pk/EsURAOuGdk5IJCd/vnzKrR7S3Txsw==} + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -3278,6 +3290,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -3326,6 +3341,11 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + sitemap@8.0.0: + resolution: {integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + smol-toml@1.3.1: resolution: {integrity: sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==} engines: {node: '>= 18'} @@ -3371,6 +3391,9 @@ packages: stacktrace-js@2.0.2: resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3961,6 +3984,12 @@ snapshots: fast-xml-parser: 5.2.5 kleur: 4.1.5 + '@astrojs/sitemap@3.4.1': + dependencies: + sitemap: 8.0.0 + stream-replace-string: 2.0.0 + zod: 3.24.2 + '@astrojs/tailwind@6.0.2(astro@5.9.4(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(rollup@4.40.0)(sass@1.86.3)(typescript@5.8.3)(yaml@2.7.1))(tailwindcss@3.4.17)': dependencies: astro: 5.9.4(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(rollup@4.40.0)(sass@1.86.3)(typescript@5.8.3)(yaml@2.7.1) @@ -5202,6 +5231,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/node@17.0.45': {} + '@types/node@22.14.1': dependencies: undici-types: 6.21.0 @@ -5224,6 +5255,10 @@ snapshots: dependencies: htmlparser2: 8.0.2 + '@types/sax@1.2.7': + dependencies: + '@types/node': 22.14.1 + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} @@ -7555,6 +7590,8 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.1 + sax@1.4.1: {} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -7622,6 +7659,13 @@ snapshots: sisteransi@1.0.5: {} + sitemap@8.0.0: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + smol-toml@1.3.1: {} snake-case@3.0.4: @@ -7660,6 +7704,8 @@ snapshots: stack-generator: 2.0.10 stacktrace-gps: 3.1.2 + stream-replace-string@2.0.0: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 787975ea6..58820175d 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -57,7 +57,7 @@ if (title) { - + @@ -84,6 +84,12 @@ if (title) { title={titleName} href={`${Astro.url.origin}/posts/feed.xml`} /> + + + + + const combined_items: RSSItem[] = [] + + posts.map(post => combined_items.push({ + title: post.data.title, + link: `/posts/${post.slug}/`, + author: post.data.author, + pubDate: new Date(`${post.data.year || 2018}-${post.data.month || 1}-${post.data.day || 1}`), + tags: post.data.tags || [], + content: sanitizeHtml(parser.render(post.body), { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img", "p", "h1", "h2", "h3", "a", "ul", "ol", "li", "blockquote", "code", "pre", "strong"]) + }), + })) + + grants.map(grant => combined_items.push({ + title: grant.data.name, + link: "/grants/", + pubDate: new Date(`${grant.data.year || 2018}-${grant.data.month || 1}-${grant.data.day || 1}`), + tags: grant.data.tags || [], + content: sanitizeHtml(parser.render(grant.body), { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img", "p", "h1", "h2", "h3", "a", "ul", "ol", "li", "blockquote", "code", "pre", "strong"]) + }), + })) + + combined_items.sort((a, b) => { + return b.pubDate.getTime() - a.pubDate.getTime() + }) + + + + return rss({ + title: "Unitary Foundation Blog", + description: "Unitary Foundation is a non-profit working to create a quantum technology ecosystem that benefits the most people.", + site: context.site, + + items: combined_items.slice(0, 20).map((item: RSSItem) => ({ + title: item.title, + pubDate: item.pubDate, + author: item.author, + categories: item.tags || [], + link: item.link, + content: item.content, + })), + }); + +} \ No newline at end of file From c15ba3863b46d9e56ab7fbedc880e7afd88a0869 Mon Sep 17 00:00:00 2001 From: J <80164315+jbolns@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:01:20 +0300 Subject: [PATCH 2/5] Update src/pages/rss.xml.ts Individual slugs for RSS grant entries. Copilot recommendation. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/pages/rss.xml.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts index 28d0e9994..7328a0501 100644 --- a/src/pages/rss.xml.ts +++ b/src/pages/rss.xml.ts @@ -33,7 +33,7 @@ export async function GET(context: { site: any; }) { grants.map(grant => combined_items.push({ title: grant.data.name, - link: "/grants/", + link: `/grants/${grant.slug}/`, pubDate: new Date(`${grant.data.year || 2018}-${grant.data.month || 1}-${grant.data.day || 1}`), tags: grant.data.tags || [], content: sanitizeHtml(parser.render(grant.body), { From 93abc1ac2dbb4f81ad1fa44ad03fa3dd4912eeb7 Mon Sep 17 00:00:00 2001 From: J <80164315+jbolns@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:02:27 +0300 Subject: [PATCH 3/5] Update src/pages/rss.xml.ts Use constant for `allowedHtmlTags`. Copilot recommendation. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/pages/rss.xml.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts index 7328a0501..03bd79713 100644 --- a/src/pages/rss.xml.ts +++ b/src/pages/rss.xml.ts @@ -27,7 +27,7 @@ export async function GET(context: { site: any; }) { pubDate: new Date(`${post.data.year || 2018}-${post.data.month || 1}-${post.data.day || 1}`), tags: post.data.tags || [], content: sanitizeHtml(parser.render(post.body), { - allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img", "p", "h1", "h2", "h3", "a", "ul", "ol", "li", "blockquote", "code", "pre", "strong"]) + allowedTags: allowedHtmlTags }), })) From 92318eff3b4dc3b18fb894d059130289029508b7 Mon Sep 17 00:00:00 2001 From: J <80164315+jbolns@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:21:33 +0300 Subject: [PATCH 4/5] Delete test.mdx page & avoid type warnings in top-level rss page --- astro.config.mjs | 3 +-- src/pages/rss.xml.ts | 6 ++++-- src/pages/test.mdx | 34 ---------------------------------- 3 files changed, 5 insertions(+), 38 deletions(-) delete mode 100644 src/pages/test.mdx diff --git a/astro.config.mjs b/astro.config.mjs index 69f844d00..92f035de9 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -40,8 +40,7 @@ export default defineConfig({ react(), dotHtmlRedirects(), sitemap({ - filter: (page) => page !== 'https://unitary.foundation/test/' && - page !== 'https://unitary.foundation/author/image/', + filter: (page) => page !== 'https://unitary.foundation/author/image/', }), ], markdown: { diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts index 03bd79713..af1219cc2 100644 --- a/src/pages/rss.xml.ts +++ b/src/pages/rss.xml.ts @@ -17,6 +17,8 @@ export async function GET(context: { site: any; }) { content: z.string().optional() }); + const allowedTags = ["img", "p", "h1", "h2", "h3", "a", "ul", "ol", "li", "blockquote", "code", "pre", "strong"] + type RSSItem = z.infer const combined_items: RSSItem[] = [] @@ -27,7 +29,7 @@ export async function GET(context: { site: any; }) { pubDate: new Date(`${post.data.year || 2018}-${post.data.month || 1}-${post.data.day || 1}`), tags: post.data.tags || [], content: sanitizeHtml(parser.render(post.body), { - allowedTags: allowedHtmlTags + allowedTags: sanitizeHtml.defaults.allowedTags.concat(allowedTags) }), })) @@ -37,7 +39,7 @@ export async function GET(context: { site: any; }) { pubDate: new Date(`${grant.data.year || 2018}-${grant.data.month || 1}-${grant.data.day || 1}`), tags: grant.data.tags || [], content: sanitizeHtml(parser.render(grant.body), { - allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img", "p", "h1", "h2", "h3", "a", "ul", "ol", "li", "blockquote", "code", "pre", "strong"]) + allowedTags: sanitizeHtml.defaults.allowedTags.concat(allowedTags) }), })) diff --git a/src/pages/test.mdx b/src/pages/test.mdx deleted file mode 100644 index 93f3d9117..000000000 --- a/src/pages/test.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: '~/layouts/Layout.astro' -title: Test page -contained: true ---- - -import { PageIntro} from '~/components/Ui'; - - - -# Intro section heading one - -**This is a featured intro paragraph. You can achieve this style by making a paragraph in the intro section bold. This section can contain [links](http://www.qutip.org/) but not bold text as that is controlling the size in this special case.** - -This is a normal paragrpah which displays larger than the body text as it is part of the intro section component. This text can contain [links](http://www.qutip.org/) and can also have **bold text** applied for emphasis. - - - -## This is a heading in heading style two - -This is a standard paragraph with a [LINK](https://www.google.com). ipsum dolor sit amet, consectetur adipiscing elit. Ut rutrum justo in magna tempor elementum. Donec bibendum libero a consequat condimentum. Curabitur condimentum sem laoreet fringilla iaculis. Nullam commodo porttitor lacinia. **This is bold paragraph text** - -- this is a list item -- this is **bold** list item -- this is a long list item which has lots of words in it and will probably break onto a second or even a third line so we can see how that looks at differnt sizes adn make sure it looks as it is intended too -- this is a list item with a [LINK](https://www.google.com) or [two)](https://www.google.com) added in it. - -### This is a heading in heading style three - -Praesent laoreet hendrerit bibendum. Praesent tincidunt mauris ac mi lobortis, nec venenatis enim euismod. Fusce quis tortor tortor. Aliquam erat volutpat. Praesent non velit ex. Curabitur lobortis, massa quis condimentum lobortis, enim erat egestas justo, id vestibulum lacus nisi vitae est. Integer mattis, ex id sollicitudin congue, nibh tellus eleifend nibh, pellentesque rhoncus augue urna in mi. Aenean non velit id lectus sagittis varius. Fusce quis sem et ex tempor luctus at eget velit. Maecenas consequat laoreet porttitor - -#### This is a heading in heading style four - -Praesent laoreet hendrerit bibendum. Praesent tincidunt mauris ac mi lobortis, nec venenatis enim euismod. Fusce quis tortor tortor. Aliquam erat volutpat. Praesent non velit ex. Curabitur lobortis, massa quis condimentum lobortis, enim erat egestas justo, id vestibulum lacus nisi vitae est. Integer mattis, ex id sollicitudin congue, nibh tellus eleifend nibh, pellentesque rhoncus augue urna in mi. Aenean non velit id lectus sagittis varius. Fusce quis sem et ex tempor luctus at eget velit. Maecenas consequat laoreet porttitor From f5202b8a75ff45c4a103a911a6f73574cbad77ec Mon Sep 17 00:00:00 2001 From: J <80164315+jbolns@users.noreply.github.com> Date: Fri, 12 Sep 2025 14:46:32 +0300 Subject: [PATCH 5/5] Simpler grants-only RSS feed --- src/layouts/Layout.astro | 6 ++-- src/pages/grants/feed.xml.ts | 43 +++++++++++++++++++++++ src/pages/rss.xml.ts | 67 ------------------------------------ 3 files changed, 46 insertions(+), 70 deletions(-) create mode 100644 src/pages/grants/feed.xml.ts delete mode 100644 src/pages/rss.xml.ts diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 58820175d..90a27788e 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -81,14 +81,14 @@ if (title) { diff --git a/src/pages/grants/feed.xml.ts b/src/pages/grants/feed.xml.ts new file mode 100644 index 000000000..ae1718fb5 --- /dev/null +++ b/src/pages/grants/feed.xml.ts @@ -0,0 +1,43 @@ +import rss from "@astrojs/rss"; +import { type CollectionEntry, getCollection } from "astro:content"; +import sanitizeHtml from "sanitize-html"; +import MarkdownIt from "markdown-it"; + +interface SiteContext { + site: string; +} + +const parser = new MarkdownIt(); + +function getDateFromPost(grant: CollectionEntry<"grant">): Date { + return new Date(`${grant.data.year || 2018}-${grant.data.month || 1}-${grant.data.day || 1}`) +} + +export async function GET(context: SiteContext) { + // HTML tags allowed in content + const allowedTags = ["img", "p", "h1", "h2", "h3", "a", "ul", "ol", "li", "blockquote", "code", "pre", "strong"] + + // Get grants, order, and slice them + let grants = await getCollection("grant"); + grants = grants.sort((a, b) => { + return getDateFromPost(b).getTime() - getDateFromPost(a).getTime(); + }).slice(0, 20); + + // Return RSS feed + return rss({ + title: "Unitary Foundation Grants", + description: "Unitary Foundation is a non-profit working to create a quantum technology ecosystem that benefits the most people.", + site: context.site, + items: grants + .map((grant) => ({ + title: grant.data.name, + link: `/grants/${grant.slug}/`, + pubDate: getDateFromPost(grant), + categories: grant.data.tags || [], + content: sanitizeHtml(parser.render(grant.body), { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(allowedTags) + }), + })), + customData: `en-us`, + }); +} diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts deleted file mode 100644 index af1219cc2..000000000 --- a/src/pages/rss.xml.ts +++ /dev/null @@ -1,67 +0,0 @@ -import rss from '@astrojs/rss'; -import { z, getCollection } from 'astro:content'; -import sanitizeHtml from 'sanitize-html'; -import MarkdownIt from 'markdown-it'; -const parser = new MarkdownIt(); - -export async function GET(context: { site: any; }) { - const posts = await getCollection("blog"); - const grants = await getCollection("grant"); - - const RSSSchema = z.object({ - title: z.string(), - link: z.string(), - author: z.string().optional(), - pubDate: z.date(), - tags: z.array(z.string()).optional(), - content: z.string().optional() - }); - - const allowedTags = ["img", "p", "h1", "h2", "h3", "a", "ul", "ol", "li", "blockquote", "code", "pre", "strong"] - - type RSSItem = z.infer - const combined_items: RSSItem[] = [] - - posts.map(post => combined_items.push({ - title: post.data.title, - link: `/posts/${post.slug}/`, - author: post.data.author, - pubDate: new Date(`${post.data.year || 2018}-${post.data.month || 1}-${post.data.day || 1}`), - tags: post.data.tags || [], - content: sanitizeHtml(parser.render(post.body), { - allowedTags: sanitizeHtml.defaults.allowedTags.concat(allowedTags) - }), - })) - - grants.map(grant => combined_items.push({ - title: grant.data.name, - link: `/grants/${grant.slug}/`, - pubDate: new Date(`${grant.data.year || 2018}-${grant.data.month || 1}-${grant.data.day || 1}`), - tags: grant.data.tags || [], - content: sanitizeHtml(parser.render(grant.body), { - allowedTags: sanitizeHtml.defaults.allowedTags.concat(allowedTags) - }), - })) - - combined_items.sort((a, b) => { - return b.pubDate.getTime() - a.pubDate.getTime() - }) - - - - return rss({ - title: "Unitary Foundation Blog", - description: "Unitary Foundation is a non-profit working to create a quantum technology ecosystem that benefits the most people.", - site: context.site, - - items: combined_items.slice(0, 20).map((item: RSSItem) => ({ - title: item.title, - pubDate: item.pubDate, - author: item.author, - categories: item.tags || [], - link: item.link, - content: item.content, - })), - }); - -} \ No newline at end of file