diff --git a/common/styleguide.tsx b/common/styleguide.tsx
index 70c3fc0c5..5677fa408 100644
--- a/common/styleguide.tsx
+++ b/common/styleguide.tsx
@@ -108,7 +108,10 @@ export function A({
const linkStyles = tw`font-sans text-black underline decoration-pewter dark:text-white dark:decoration-palette-gray5`;
const linkHoverStyles = tw`decoration-primary-dark`;
- if ((target === '_self' && !href.startsWith('#')) || href.startsWith('/')) {
+ if (
+ (target === '_self' && !href.startsWith('#')) ||
+ (target !== '_blank' && href.startsWith('/'))
+ ) {
const passedStyle = StyleSheet.flatten(style);
return (
;
count?: number;
queryParams?: Query;
+ rss?: string;
};
-export default function HomeSection({ data, title, Icon, count = 8, queryParams = {} }: Props) {
+export default function HomeSection({
+ data,
+ title,
+ Icon,
+ rss,
+ count = 8,
+ queryParams = {},
+}: Props) {
const hashLink = title.replace(/\s/g, '').toLowerCase();
return (
@@ -35,6 +44,18 @@ export default function HomeSection({ data, title, Icon, count = 8, queryParams
hoverStyle={tw`text-palette-gray4 dark:text-palette-gray5`}>
{title}
+ {rss && (
+
diff --git a/components/Icons/index.tsx b/components/Icons/index.tsx
index a54c2ed45..6c000e309 100644
--- a/components/Icons/index.tsx
+++ b/components/Icons/index.tsx
@@ -1247,3 +1247,27 @@ export function FundingGitHub({ width = 24, height = 24, style }: IconProps) {
);
}
+
+export function RSS({ width = 24, height = 24, style }: IconProps) {
+ return (
+
+ );
+}
diff --git a/pages/rss/added.xml.ts b/pages/rss/added.xml.ts
new file mode 100644
index 000000000..9cc3c1c49
--- /dev/null
+++ b/pages/rss/added.xml.ts
@@ -0,0 +1,47 @@
+import { type NextPageContext } from 'next';
+
+import { type APIResponseType } from '~/types';
+import { generateLibrariesRss } from '~/util/rss';
+import { ssrFetch } from '~/util/SSRFetch';
+
+const RSS_LIMIT = 20;
+
+export async function getServerSideProps(ctx: NextPageContext) {
+ const { res } = ctx;
+
+ if (!res) {
+ return { notFound: true };
+ }
+
+ try {
+ const response = await ssrFetch(
+ '/libraries',
+ { order: 'added', limit: RSS_LIMIT.toString() },
+ ctx
+ );
+
+ const { libraries } = (await response.json()) as APIResponseType;
+
+ res.statusCode = 200;
+ res.write(
+ generateLibrariesRss(
+ 'Recently Added',
+ 'The recently added packages to the directory',
+ libraries
+ )
+ );
+ } catch {
+ res.statusCode = 500;
+ res.write('Error: Cannot generate RSS feed');
+ }
+
+ res.setHeader('Content-Type', 'text/xml; charset=utf-8');
+ res.setHeader('Cache-Control', 'public, s-maxage=600, stale-while-revalidate=300');
+ res.end();
+
+ return { props: {} };
+}
+
+export default function RSSFeedAdded() {
+ return null;
+}
diff --git a/pages/rss/updated.xml.ts b/pages/rss/updated.xml.ts
new file mode 100644
index 000000000..0c6163927
--- /dev/null
+++ b/pages/rss/updated.xml.ts
@@ -0,0 +1,47 @@
+import { type NextPageContext } from 'next';
+
+import { type APIResponseType } from '~/types';
+import { generateLibrariesRss } from '~/util/rss';
+import { ssrFetch } from '~/util/SSRFetch';
+
+const RSS_LIMIT = 20;
+
+export async function getServerSideProps(ctx: NextPageContext) {
+ const { res } = ctx;
+
+ if (!res) {
+ return { notFound: true };
+ }
+
+ try {
+ const response = await ssrFetch(
+ '/libraries',
+ { order: 'updated', limit: RSS_LIMIT.toString() },
+ ctx
+ );
+
+ const { libraries } = (await response.json()) as APIResponseType;
+
+ res.statusCode = 200;
+ res.write(
+ generateLibrariesRss(
+ 'Just Updated',
+ 'The recently updated packages in the directory',
+ libraries
+ )
+ );
+ } catch {
+ res.statusCode = 500;
+ res.write('Error: Cannot generate RSS feed');
+ }
+
+ res.setHeader('Content-Type', 'text/xml; charset=utf-8');
+ res.setHeader('Cache-Control', 'public, s-maxage=600, stale-while-revalidate=300');
+ res.end();
+
+ return { props: {} };
+}
+
+export default function RSSFeedAdded() {
+ return null;
+}
diff --git a/scenes/HomeScene.tsx b/scenes/HomeScene.tsx
index 36fefb9f7..dc537a7c7 100644
--- a/scenes/HomeScene.tsx
+++ b/scenes/HomeScene.tsx
@@ -167,12 +167,14 @@ export default function HomeScene({
title="Just updated"
Icon={Calendar}
queryParams={{ order: 'updated' }}
+ rss="/rss/updated.xml"
/>