diff --git a/lib/__tests__/resolve-page-id.test.ts b/lib/__tests__/resolve-page-id.test.ts index a056583fdb..3fd1223025 100644 --- a/lib/__tests__/resolve-page-id.test.ts +++ b/lib/__tests__/resolve-page-id.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it } from 'vitest' -import { lookupSlug, resolvePageIdFromMaps } from '../resolve-page-id' +import { + isResolvablePageSlug, + lookupSlug, + resolvePageIdFromMaps +} from '../resolve-page-id' const maps = { pageUrlOverrides: { @@ -50,3 +54,23 @@ describe('lookupSlug', () => { ).toBe('2bc5cb08-cf2c-810b-83af-c01fde10aefa') }) }) + +describe('isResolvablePageSlug', () => { + it('accepts slugs in the committed index', () => { + expect(isResolvablePageSlug('oct-25', maps)).toBe(true) + }) + + it('accepts slugs from override-page collections', () => { + expect( + isResolvablePageSlug('rapid-prototyping-highlighty', maps, { + 'rapid-prototyping-highlighty': '2bc5cb08-cf2c-810b-83af-c01fde10aefa' + }) + ).toBe(true) + }) + + it('rejects leftover posts that are not on any public listing', () => { + expect( + isResolvablePageSlug('rapid-prototyping-highlighty-part-2', maps) + ).toBe(false) + }) +}) diff --git a/lib/resolve-page-id.ts b/lib/resolve-page-id.ts index d6dc425d12..36b0038be6 100644 --- a/lib/resolve-page-id.ts +++ b/lib/resolve-page-id.ts @@ -47,3 +47,18 @@ export function lookupSlug( const pageId = slugMap[slug] || slugMap[normalized] return pageId ? (parsePageId(pageId) ?? undefined) : undefined } + +/** + * True when `/` would resolve the same way `resolveNotionPage` does: + * committed index / URL overrides, then collection slugs from override pages. + * Used to keep the RSS feed from advertising leftover posts that 404. + */ +export function isResolvablePageSlug( + slug: string, + maps: PageIdLookupMaps, + extraSlugMap: Record = {} +): boolean { + return Boolean( + resolvePageIdFromMaps(slug, maps) || lookupSlug(slug, extraSlugMap) + ) +} diff --git a/pages/feed.tsx b/pages/feed.tsx index a654c8096f..4f67485725 100644 --- a/pages/feed.tsx +++ b/pages/feed.tsx @@ -12,8 +12,13 @@ import RSS from 'rss' import * as config from '@/lib/config' import { getSocialImageUrl } from '@/lib/get-social-image-url' import { notion } from '@/lib/notion-api' +import { canonicalPageMap } from '@/lib/notion-index' import { getPageSlug } from '@/lib/page-slug' -import { getCollectionBlockIds } from '@/lib/posts-collection' +import { + getCollectionBlockIds, + getOverrideCollectionSlugMap +} from '@/lib/posts-collection' +import { isResolvablePageSlug } from '@/lib/resolve-page-id' export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { if (req.method !== 'GET') { @@ -94,6 +99,13 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { return { props: {} } } + const extraSlugMap = await getOverrideCollectionSlugMap() + const pageIdMaps = { + pageUrlOverrides: config.pageUrlOverrides, + pageUrlAdditions: config.pageUrlAdditions, + canonicalPageMap + } + // Get all pages that belong to this collection for (const blockId of collectionBlockIds) { const block = @@ -124,6 +136,10 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { config.description const slug = getPageSlug(block, recordMap) || pageId + if (!isResolvablePageSlug(slug, pageIdMaps, extraSlugMap)) { + console.warn('skipping feed item with unresolvable slug', slug) + continue + } const url = `${config.host}/${slug}` const lastUpdatedTime = getPageProperty(