feat(library): add static pagination and page-one redirects
This commit is contained in:
18 files changed
+895
-45
No files matched your search
@@ -0,0 +1,53 @@
|
||||
---
|
||||
import {
|
||||
libraryPageRoute,
|
||||
type LibraryPageSlice
|
||||
} from '../utils/library-pagination';
|
||||
import type { LibraryLocale } from '../utils/library-catalog';
|
||||
|
||||
interface Props {
|
||||
locale: LibraryLocale;
|
||||
page: LibraryPageSlice<unknown>;
|
||||
}
|
||||
|
||||
const { locale, page } = Astro.props;
|
||||
const messages = {
|
||||
en: { navigation: 'Library pagination', previous: 'Previous', next: 'Next', page: 'Page', current: 'current page' },
|
||||
es: { navigation: 'Paginación de la biblioteca', previous: 'Anterior', next: 'Siguiente', page: 'Página', current: 'página actual' },
|
||||
ar: { navigation: 'ترقيم صفحات المكتبة', previous: 'السابق', next: 'التالي', page: 'الصفحة', current: 'الصفحة الحالية' }
|
||||
}[locale];
|
||||
const pages = Array.from({ length: page.totalPages }, (_, index) => index + 1);
|
||||
---
|
||||
{page.totalPages > 1 && (
|
||||
<nav class="library-pagination" aria-label={messages.navigation}>
|
||||
<ul>
|
||||
{page.previousPage && (
|
||||
<li class="library-pagination__previous">
|
||||
<a rel="prev" href={libraryPageRoute(locale, page.previousPage)}>{messages.previous}</a>
|
||||
</li>
|
||||
)}
|
||||
{pages.map((number) => (
|
||||
<li>
|
||||
<a
|
||||
href={libraryPageRoute(locale, number)}
|
||||
aria-current={number === page.currentPage ? 'page' : undefined}
|
||||
aria-label={`${messages.page} ${number}${number === page.currentPage ? `, ${messages.current}` : ''}`}
|
||||
>{number}</a>
|
||||
</li>
|
||||
))}
|
||||
{page.nextPage && (
|
||||
<li class="library-pagination__next">
|
||||
<a rel="next" href={libraryPageRoute(locale, page.nextPage)}>{messages.next}</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
)}
|
||||
|
||||
<style>
|
||||
.library-pagination { margin-top: 2rem; }
|
||||
.library-pagination ul { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: center; list-style: none; margin: 0; padding: 0; }
|
||||
.library-pagination a { align-items: center; border: 2px solid var(--color-primary); border-radius: 4px; color: var(--color-primary); display: inline-flex; font-weight: 700; justify-content: center; min-height: 44px; min-width: 44px; padding: .5rem .75rem; text-decoration: none; }
|
||||
.library-pagination a[aria-current="page"] { background: var(--color-primary); color: white; }
|
||||
.library-pagination a:focus-visible { outline: 3px solid var(--color-accent); outline-offset: 2px; }
|
||||
</style>
|
||||
@@ -1,13 +1,25 @@
|
||||
---
|
||||
import { getLibraryCatalog } from '../../utils/library';
|
||||
import { paginateLibraryItems } from '../../utils/library-pagination';
|
||||
import BlogIndexLayout from '../../layouts/BlogIndexLayout.astro';
|
||||
import BlogCard from '../BlogCard.astro';
|
||||
import LibrarySearch from '../LibrarySearch.astro';
|
||||
const posts = (await getLibraryCatalog()).byLocale.ar.map(({ entry }) => entry);
|
||||
const popular = posts.slice(0, 5);
|
||||
import LibraryPagination from '../LibraryPagination.astro';
|
||||
interface Props { page?: number; }
|
||||
const { page = 1 } = Astro.props;
|
||||
const allPosts = (await getLibraryCatalog()).byLocale.ar;
|
||||
const pagination = paginateLibraryItems(allPosts, page);
|
||||
if (!pagination) throw new Error(`Invalid Arabic Library page: ${page}`);
|
||||
const posts = pagination.items.map(({ entry }) => entry);
|
||||
const popular = allPosts.slice(0, 5).map(({ entry }) => entry);
|
||||
const heading = 'مكتبة معهد أريزونا للتوحد';
|
||||
const title = page === 1 ? heading : `${heading} | الصفحة ${page}`;
|
||||
---
|
||||
<BlogIndexLayout title="مكتبة معهد أريزونا للتوحد" description="مقالات إخبارية حول علاج تحليل السلوك التطبيقي والتعليم الخاص، ودراسات حالة، ووصفات، ومقالات افتتاحية، ودروس تعليمية." lang="ar">
|
||||
<BlogIndexLayout title={title} heading={heading} description="مقالات إخبارية حول علاج تحليل السلوك التطبيقي والتعليم الخاص، ودراسات حالة، ووصفات، ومقالات افتتاحية، ودروس تعليمية." lang="ar">
|
||||
<div id="library-results-ar" class="library-search-results" hidden></div>
|
||||
<div id="library-archive-ar" class="blog-list">{posts.map((post) => <BlogCard post={post} prefix="/ar" />)}</div>
|
||||
<div id="library-archive-ar" class="library-archive">
|
||||
<div class="blog-list">{posts.map((post) => <BlogCard post={post} prefix="/ar" />)}</div>
|
||||
<LibraryPagination locale="ar" page={pagination} />
|
||||
</div>
|
||||
<aside class="library-sidebar"><LibrarySearch locale="ar" archiveId="library-archive-ar" resultsId="library-results-ar" /><h2>المقالات الشائعة</h2><ul>{popular.map((post) => <li><a href={`/ar/library/${post.data.slug}`}>{post.data.title}</a></li>)}</ul></aside>
|
||||
</BlogIndexLayout>
|
||||
@@ -1,13 +1,23 @@
|
||||
---
|
||||
import { getLibraryCatalog } from '../../utils/library';
|
||||
import { paginateLibraryItems } from '../../utils/library-pagination';
|
||||
import BlogIndexLayout from '../../layouts/BlogIndexLayout.astro';
|
||||
import BlogCard from '../BlogCard.astro';
|
||||
import LibrarySearch from '../LibrarySearch.astro';
|
||||
const posts = (await getLibraryCatalog()).byLocale.en.map(({ entry }) => entry);
|
||||
const popular = posts.slice(0, 5);
|
||||
import LibraryPagination from '../LibraryPagination.astro';
|
||||
interface Props { page?: number; }
|
||||
const { page = 1 } = Astro.props;
|
||||
const allPosts = (await getLibraryCatalog()).byLocale.en;
|
||||
const pagination = paginateLibraryItems(allPosts, page);
|
||||
if (!pagination) throw new Error(`Invalid English Library page: ${page}`);
|
||||
const posts = pagination.items.map(({ entry }) => entry);
|
||||
const popular = allPosts.slice(0, 5).map(({ entry }) => entry);
|
||||
const heading = 'The Library at AZ Institute for Autism';
|
||||
const title = page === 1 ? heading : `${heading} | Page ${page}`;
|
||||
---
|
||||
<BlogIndexLayout
|
||||
title="The Library at AZ Institute for Autism"
|
||||
title={title}
|
||||
heading={heading}
|
||||
description="Welcome to the Library at AIA, where we share valuable insights, strategies, and resources to support individuals with autism, their families, and professionals in the field. Our articles explore key topics in Applied Behavior Analysis (ABA) therapy, community events, success stories, and expert guidance."
|
||||
intro={[
|
||||
"Welcome to the Library at AIA, where we share valuable insights, strategies, and resources to support individuals with autism, their families, and professionals in the field. Our articles explore key topics in Applied Behavior Analysis (ABA) therapy, including teaching self-advocacy, proactive and reactive strategies, and innovative intervention techniques.",
|
||||
@@ -15,7 +25,10 @@ const popular = posts.slice(0, 5);
|
||||
]}
|
||||
>
|
||||
<div id="library-results-en" class="library-search-results" hidden></div>
|
||||
<div id="library-archive-en" class="blog-list">{posts.map((post) => <BlogCard post={post} />)}</div>
|
||||
<div id="library-archive-en" class="library-archive">
|
||||
<div class="blog-list">{posts.map((post) => <BlogCard post={post} />)}</div>
|
||||
<LibraryPagination locale="en" page={pagination} />
|
||||
</div>
|
||||
<aside class="library-sidebar">
|
||||
<LibrarySearch locale="en" archiveId="library-archive-en" resultsId="library-results-en" />
|
||||
<h2>Popular Posts</h2>
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
---
|
||||
import { getLibraryCatalog } from '../../utils/library';
|
||||
import { paginateLibraryItems } from '../../utils/library-pagination';
|
||||
import BlogIndexLayout from '../../layouts/BlogIndexLayout.astro';
|
||||
import BlogCard from '../BlogCard.astro';
|
||||
import LibrarySearch from '../LibrarySearch.astro';
|
||||
const posts = (await getLibraryCatalog()).byLocale.es.map(({ entry }) => entry);
|
||||
const popular = posts.slice(0, 5);
|
||||
import LibraryPagination from '../LibraryPagination.astro';
|
||||
interface Props { page?: number; }
|
||||
const { page = 1 } = Astro.props;
|
||||
const allPosts = (await getLibraryCatalog()).byLocale.es;
|
||||
const pagination = paginateLibraryItems(allPosts, page);
|
||||
if (!pagination) throw new Error(`Invalid Spanish Library page: ${page}`);
|
||||
const posts = pagination.items.map(({ entry }) => entry);
|
||||
const popular = allPosts.slice(0, 5).map(({ entry }) => entry);
|
||||
const heading = 'La Biblioteca en AZ Institute for Autism';
|
||||
const title = page === 1 ? heading : `${heading} | Página ${page}`;
|
||||
---
|
||||
<BlogIndexLayout title="La Biblioteca en AZ Institute for Autism" description="Noticias sobre terapia ABA y educación especial, casos de estudio, recetas, editoriales y tutoriales para y sobre nuestros estudiantes con autismo." lang="es">
|
||||
<BlogIndexLayout title={title} heading={heading} description="Noticias sobre terapia ABA y educación especial, casos de estudio, recetas, editoriales y tutoriales para y sobre nuestros estudiantes con autismo." lang="es">
|
||||
<div id="library-results-es" class="library-search-results" hidden></div>
|
||||
<div id="library-archive-es" class="blog-list">{posts.map((post) => <BlogCard post={post} prefix="/es" />)}</div>
|
||||
<div id="library-archive-es" class="library-archive">
|
||||
<div class="blog-list">{posts.map((post) => <BlogCard post={post} prefix="/es" />)}</div>
|
||||
<LibraryPagination locale="es" page={pagination} />
|
||||
</div>
|
||||
<aside class="library-sidebar"><LibrarySearch locale="es" archiveId="library-archive-es" resultsId="library-results-es" /><h2>Publicaciones populares</h2><ul>{popular.map((post) => <li><a href={`/es/library/${post.data.slug}`}>{post.data.title}</a></li>)}</ul></aside>
|
||||
</BlogIndexLayout>
|
||||
@@ -3,18 +3,36 @@
|
||||
"from": "/aba",
|
||||
"to": "/aba-therapy",
|
||||
"status": 301,
|
||||
"reason": "Brief alias to preserved mirror URL"
|
||||
"reason": "Brief legacy alias to preserved URL"
|
||||
},
|
||||
{
|
||||
"from": "/autismevaluations",
|
||||
"to": "/autism-evaluations",
|
||||
"status": 301,
|
||||
"reason": "Brief alias to preserved mirror URL"
|
||||
"reason": "Brief legacy alias to preserved URL"
|
||||
},
|
||||
{
|
||||
"from": "/learnersocialclub",
|
||||
"to": "/learner-social-club",
|
||||
"status": 301,
|
||||
"reason": "Brief alias to preserved mirror URL"
|
||||
"reason": "Brief legacy alias to preserved URL"
|
||||
},
|
||||
{
|
||||
"from": "/library/page/1",
|
||||
"to": "/library",
|
||||
"status": 301,
|
||||
"reason": "Library first-page alias"
|
||||
},
|
||||
{
|
||||
"from": "/es/library/page/1",
|
||||
"to": "/es/library",
|
||||
"status": 301,
|
||||
"reason": "Spanish Library first-page alias"
|
||||
},
|
||||
{
|
||||
"from": "/ar/library/page/1",
|
||||
"to": "/ar/library",
|
||||
"status": 301,
|
||||
"reason": "Arabic Library first-page alias"
|
||||
}
|
||||
]
|
||||
@@ -5,16 +5,17 @@ import { getPublication } from '../utils/publication';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
heading?: string;
|
||||
description: string;
|
||||
intro?: string[];
|
||||
lang?: 'en' | 'ar' | 'es';
|
||||
}
|
||||
|
||||
const { title, description, intro = [], lang = 'en' } = Astro.props;
|
||||
const { title, heading, description, intro = [], lang = 'en' } = Astro.props;
|
||||
const { entry } = await getPublication(Astro.url.pathname);
|
||||
---
|
||||
<BaseLayout title={title} description={description} lang={lang}>
|
||||
<PageHero title={entry.data.displayH1 ?? title} image="/assets/images/hero-library-index.webp" eyebrow="Welcome to" constrain />
|
||||
<PageHero title={entry.data.displayH1 ?? heading ?? title} image="/assets/images/hero-library-index.webp" eyebrow="Welcome to" constrain />
|
||||
<section class="library-intro">
|
||||
<div class="container">
|
||||
<h2>Library</h2>
|
||||
@@ -34,7 +35,7 @@ const { entry } = await getPublication(Astro.url.pathname);
|
||||
:global(.library-sidebar li) { border-bottom: 1px solid var(--color-border); padding-block: .65rem; }
|
||||
:global(.library-sidebar a) { color: var(--color-primary); font-size: .9rem; }
|
||||
:global(.blog-list) { display: block; }
|
||||
:global(.library-search-results[hidden]), :global(.blog-list[hidden]) { display: none; }
|
||||
:global(.library-search-results[hidden]), :global(.library-archive[hidden]), :global(.blog-list[hidden]) { display: none; }
|
||||
:global(.library-search-results h2) { margin-top: 0; }
|
||||
:global(.library-search-result) { border-bottom: 1px solid var(--color-border); margin-bottom: 2rem; padding-bottom: 1.5rem; }
|
||||
:global(.library-search-result h3) { font-family: var(--font-body); margin-bottom: .65rem; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
import { render } from 'astro:content';
|
||||
import { getPublicationManifest } from '../utils/publication';
|
||||
import { render, type CollectionEntry } from 'astro:content';
|
||||
import { getRoutePublicationManifest } from '../utils/publication';
|
||||
import type { LibraryPageDescriptor } from '../utils/library-pagination';
|
||||
import PageLayout from '../layouts/PageLayout.astro';
|
||||
import BlogPostLayout from '../layouts/BlogPostLayout.astro';
|
||||
import HomePage from '../components/pages/HomePage.astro';
|
||||
@@ -9,12 +10,18 @@ import LibraryEnglish from '../components/pages/LibraryEnglish.astro';
|
||||
import LibrarySpanish from '../components/pages/LibrarySpanish.astro';
|
||||
import LibraryArabic from '../components/pages/LibraryArabic.astro';
|
||||
export async function getStaticPaths() {
|
||||
return (await getPublicationManifest()).filter((page) => page.eligible).map(({ route, entry }) => ({
|
||||
params: { slug: route === '/' ? undefined : decodeURI(route.slice(1)) },
|
||||
props: { entry }
|
||||
return (await getRoutePublicationManifest()).filter((page) => page.eligible).map((publication) => ({
|
||||
params: { slug: publication.route === '/' ? undefined : decodeURI(publication.route.slice(1)) },
|
||||
props: {
|
||||
entry: publication.entry,
|
||||
libraryPage: 'libraryPage' in publication ? publication.libraryPage : undefined
|
||||
}
|
||||
}));
|
||||
}
|
||||
const { entry } = Astro.props;
|
||||
const { entry, libraryPage } = Astro.props as {
|
||||
entry: CollectionEntry<'pages'> | CollectionEntry<'blog'>;
|
||||
libraryPage?: LibraryPageDescriptor;
|
||||
};
|
||||
const { Content } = await render(entry);
|
||||
---
|
||||
{entry.collection === 'blog'
|
||||
@@ -22,5 +29,5 @@ const { Content } = await render(entry);
|
||||
: entry.data.slug === 'index'
|
||||
? entry.data.lang === 'ar' ? <ArabicHome /> : <HomePage entry={entry} />
|
||||
: entry.data.slug === 'library'
|
||||
? entry.data.lang === 'es' ? <LibrarySpanish /> : entry.data.lang === 'ar' ? <LibraryArabic /> : <LibraryEnglish />
|
||||
? entry.data.lang === 'es' ? <LibrarySpanish page={libraryPage?.currentPage} /> : entry.data.lang === 'ar' ? <LibraryArabic page={libraryPage?.currentPage} /> : <LibraryEnglish page={libraryPage?.currentPage} />
|
||||
: <PageLayout entry={entry}><Content /></PageLayout>}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { allowIndexing, getPublicationManifest } from '../utils/publication';
|
||||
import { allowIndexing, getRoutePublicationManifest } from '../utils/publication';
|
||||
import { renderSitemap } from '../utils/publication-policy';
|
||||
|
||||
export async function GET() {
|
||||
return new Response(renderSitemap(await getPublicationManifest(), allowIndexing), {
|
||||
return new Response(renderSitemap(await getRoutePublicationManifest(), allowIndexing), {
|
||||
headers: { 'Content-Type': 'application/xml; charset=utf-8' }
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import {
|
||||
publicationFor,
|
||||
type PublicationEntry,
|
||||
type TranslatedPublication
|
||||
} from './publication-policy.ts';
|
||||
import {
|
||||
libraryLocales,
|
||||
type LibraryCatalog,
|
||||
type LibraryBlogEntry,
|
||||
type LibraryLocale
|
||||
} from './library-catalog.ts';
|
||||
|
||||
export const LIBRARY_PAGE_SIZE = 10;
|
||||
|
||||
export interface LibraryPageSlice<T> {
|
||||
currentPage: number;
|
||||
totalItems: number;
|
||||
/** Number of nonempty pages. A valid empty root archive reports zero. */
|
||||
totalPages: number;
|
||||
previousPage: number | null;
|
||||
nextPage: number | null;
|
||||
items: T[];
|
||||
}
|
||||
|
||||
export interface LibraryPageDescriptor {
|
||||
locale: LibraryLocale;
|
||||
currentPage: number;
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export type ContentPublication<TEntry extends PublicationEntry = PublicationEntry> = TranslatedPublication & { entry: TEntry };
|
||||
export type LibraryPaginationPublication<TEntry extends PublicationEntry = PublicationEntry> = ContentPublication<TEntry> & {
|
||||
libraryPage: LibraryPageDescriptor;
|
||||
};
|
||||
|
||||
export function paginateLibraryItems<T>(
|
||||
items: readonly T[],
|
||||
requestedPage: number,
|
||||
pageSize = LIBRARY_PAGE_SIZE
|
||||
): LibraryPageSlice<T> | null {
|
||||
if (!Number.isSafeInteger(requestedPage) || requestedPage < 1
|
||||
|| !Number.isSafeInteger(pageSize) || pageSize < 1) return null;
|
||||
const totalItems = items.length;
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
// The unnumbered root remains a valid empty archive. Numbered routes never do.
|
||||
if ((totalPages === 0 && requestedPage !== 1) || (totalPages > 0 && requestedPage > totalPages)) return null;
|
||||
const start = (requestedPage - 1) * pageSize;
|
||||
return {
|
||||
currentPage: requestedPage,
|
||||
totalItems,
|
||||
totalPages,
|
||||
previousPage: requestedPage > 1 ? requestedPage - 1 : null,
|
||||
nextPage: requestedPage < totalPages ? requestedPage + 1 : null,
|
||||
items: items.slice(start, start + pageSize)
|
||||
};
|
||||
}
|
||||
|
||||
export function libraryRootRoute(locale: LibraryLocale): string {
|
||||
return locale === 'en' ? '/library' : `/${locale}/library`;
|
||||
}
|
||||
|
||||
export function libraryPageRoute(locale: LibraryLocale, page: number): string {
|
||||
if (!Number.isSafeInteger(page) || page < 1) throw new Error(`Invalid Library page number: ${page}`);
|
||||
const root = libraryRootRoute(locale);
|
||||
return page === 1 ? root : `${root}/page/${page}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose generated page 2+ publications from the content manifest and catalog.
|
||||
* The content manifest stays independent, preventing a catalog/manifest cycle.
|
||||
*/
|
||||
export function createLibraryPaginationPublications<
|
||||
TBlog extends LibraryBlogEntry,
|
||||
TEntry extends PublicationEntry
|
||||
>(
|
||||
catalog: LibraryCatalog<TBlog>,
|
||||
manifest: ContentPublication<TEntry>[],
|
||||
site: string
|
||||
): LibraryPaginationPublication<TEntry>[] {
|
||||
const contentRoutes = new Map(manifest.map((page) => [page.route, `${page.entry.collection}/${page.entry.id}`]));
|
||||
const publications: LibraryPaginationPublication<TEntry>[] = [];
|
||||
|
||||
for (const locale of libraryLocales) {
|
||||
const rootRoute = libraryRootRoute(locale);
|
||||
const root = manifest.find((page) => page.route === rootRoute);
|
||||
if (!root || root.entry.collection !== 'pages' || root.entry.data.lang !== locale || root.entry.data.slug !== 'library') {
|
||||
throw new Error(`Missing Library root publication: ${rootRoute}`);
|
||||
}
|
||||
|
||||
// Reserve the complete family, including aliases and malformed content
|
||||
// candidates, so content can never silently shadow generated archives.
|
||||
const familyPrefix = `${rootRoute}/page`;
|
||||
const collision = manifest.find((page) => page.route === familyPrefix || page.route.startsWith(`${familyPrefix}/`));
|
||||
if (collision) throw new Error(`Reserved Library pagination route collision: ${collision.route} (${contentRoutes.get(collision.route)})`);
|
||||
if (!root.eligible) continue;
|
||||
|
||||
const items = catalog.byLocale[locale];
|
||||
const first = paginateLibraryItems(items, 1);
|
||||
if (!first) throw new Error(`Unable to paginate Library root: ${rootRoute}`);
|
||||
for (let currentPage = 2; currentPage <= first.totalPages; currentPage++) {
|
||||
const page = paginateLibraryItems(items, currentPage);
|
||||
if (!page?.items.length) throw new Error(`Empty generated Library page: ${libraryPageRoute(locale, currentPage)}`);
|
||||
const synthetic: PublicationEntry = {
|
||||
id: `generated/${locale}/library/page/${currentPage}`,
|
||||
collection: 'pages',
|
||||
data: { lang: locale, slug: `library/page/${currentPage}`, draft: false }
|
||||
};
|
||||
const publication = publicationFor(synthetic, site);
|
||||
const previous = contentRoutes.get(publication.route);
|
||||
if (previous) throw new Error(`Route collision: ${publication.route} (${previous}, ${synthetic.id})`);
|
||||
contentRoutes.set(publication.route, synthetic.id);
|
||||
publications.push({
|
||||
...publication,
|
||||
entry: root.entry,
|
||||
translations: [],
|
||||
libraryPage: {
|
||||
locale,
|
||||
currentPage,
|
||||
totalItems: page.totalItems,
|
||||
totalPages: page.totalPages
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return publications.sort((left, right) => left.route.localeCompare(right.route, 'en'));
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { getCollection } from 'astro:content';
|
||||
import { getCollection, type CollectionEntry } from 'astro:content';
|
||||
import site from '../data/site.json';
|
||||
import { createPublicationManifest, normalizeRoute } from './publication-policy';
|
||||
import { createLibraryCatalog } from './library-catalog';
|
||||
import { createLibraryPaginationPublications } from './library-pagination';
|
||||
import { createLibrarySearchPublication } from './search-publication';
|
||||
|
||||
export const allowIndexing = import.meta.env.PUBLIC_ALLOW_INDEXING === 'true';
|
||||
@@ -10,10 +12,29 @@ export async function getPublicationManifest() {
|
||||
return createPublicationManifest([...pages, ...posts], site.url);
|
||||
}
|
||||
|
||||
export async function getLibraryPaginationManifest(
|
||||
manifest?: Awaited<ReturnType<typeof getPublicationManifest>>
|
||||
) {
|
||||
manifest ??= await getPublicationManifest();
|
||||
const authors = await getCollection('authors');
|
||||
const catalog = createLibraryCatalog(
|
||||
manifest as Parameters<typeof createLibraryCatalog<CollectionEntry<'blog'>>>[0],
|
||||
authors
|
||||
);
|
||||
return createLibraryPaginationPublications(catalog, manifest, site.url);
|
||||
}
|
||||
|
||||
/** Content publications plus deterministic generated archive routes. */
|
||||
export async function getRoutePublicationManifest() {
|
||||
const content = await getPublicationManifest();
|
||||
const pagination = await getLibraryPaginationManifest(content);
|
||||
return [...content, ...pagination].sort((left, right) => left.route.localeCompare(right.route, 'en'));
|
||||
}
|
||||
|
||||
export async function getPublication(route: string) {
|
||||
const normalized = normalizeRoute(route);
|
||||
if (normalized === '/search') return createLibrarySearchPublication(site.url);
|
||||
const publication = (await getPublicationManifest()).find((page) => page.route === normalized);
|
||||
const publication = (await getRoutePublicationManifest()).find((page) => page.route === normalized);
|
||||
if (!publication?.eligible) throw new Error(`Route is not published: ${normalized}`);
|
||||
return publication;
|
||||
}
|
||||
Reference in new issue
Block a user