111 lines
3.9 KiB
JavaScript
111 lines
3.9 KiB
JavaScript
import { getPayload } from 'payload';
|
|
import { cache } from 'react';
|
|
import { getArchiveEntries, resolveRoute as resolveRouteRaw } from '../content/index.js';
|
|
import { buildRobots, buildSitemapEntries } from '../seo/index.js';
|
|
/**
|
|
* Bundles the per-request data helpers a frontend needs — the same cached
|
|
* wrappers every project was writing by hand (getPayload, settings, locale
|
|
* list, route resolution, archive entries).
|
|
*
|
|
* Everything is wrapped in React `cache()`, so within one request a value is
|
|
* fetched once no matter how many times it's asked for — which matters because
|
|
* Next runs generateMetadata and the page component separately, and both hit
|
|
* these. Crucially the Payload instance is cached *here*, once, so every helper
|
|
* shares it; that's why this is a factory and not loose functions importing a
|
|
* shared module.
|
|
*
|
|
* ```ts
|
|
* // src/lib/content.ts
|
|
* import { createContentHelpers } from 'ipal-kit'
|
|
* import config from '@/payload.config'
|
|
* import { contentConfig } from '@/content.config'
|
|
*
|
|
* export const { getCachedPayload, getSettings, getConfiguredLocales, resolveRoute, getEntries } =
|
|
* createContentHelpers({ config, content: contentConfig })
|
|
* ```
|
|
*
|
|
* `resolveRoute` and `getEntries` are separate on purpose: routing decides what
|
|
* a URL is, fetching gets the listing. Metadata generation needs the first and
|
|
* not the second, and a page component composes them in two obvious lines.
|
|
*/ export function createContentHelpers({ baseUrl, config, content, i18n, pagesSlug = 'pages', settingsSlug = 'site-settings' }) {
|
|
const origin = baseUrl ?? process.env.NEXT_PUBLIC_SERVER_URL ?? '';
|
|
const getCachedPayload = cache(async ()=>getPayload({
|
|
config: await config
|
|
}));
|
|
const getConfiguredLocales = cache(async ()=>{
|
|
const c = await config;
|
|
return c.localization ? c.localization.locales.map((l)=>l.code) : [];
|
|
});
|
|
const getSettings = cache(async (locale)=>{
|
|
const payload = await getCachedPayload();
|
|
return payload.findGlobal({
|
|
slug: settingsSlug,
|
|
depth: 2,
|
|
locale: locale
|
|
});
|
|
});
|
|
/** What does this URL point at? Routing only — no listing data. */ const resolveRoute = cache(async (locale, segments, page)=>{
|
|
const payload = await getCachedPayload();
|
|
return resolveRouteRaw({
|
|
content,
|
|
locale,
|
|
page,
|
|
pagesSlug,
|
|
payload,
|
|
segments,
|
|
settingsSlug
|
|
});
|
|
});
|
|
/** One page of a collection's entries, for an archive listing. */ const getEntries = cache(async (collection, locale, page, perPage)=>{
|
|
const payload = await getCachedPayload();
|
|
return getArchiveEntries({
|
|
collection,
|
|
locale,
|
|
page,
|
|
payload,
|
|
perPage
|
|
});
|
|
});
|
|
/**
|
|
* Ready-made handler for Next's `app/sitemap.ts` — every page and entry with
|
|
* per-URL hreflang and lastmod. Re-export it directly:
|
|
*
|
|
* ```ts
|
|
* // app/sitemap.ts
|
|
* export { sitemap as default } from '@/lib/content'
|
|
* ```
|
|
*/ const sitemap = cache(async ()=>{
|
|
if (!i18n) {
|
|
throw new Error('[ipal] createContentHelpers: pass `i18n` to use the sitemap handler.');
|
|
}
|
|
return buildSitemapEntries({
|
|
baseUrl: origin,
|
|
config: i18n,
|
|
content,
|
|
pagesSlug,
|
|
payload: await getCachedPayload(),
|
|
settingsSlug
|
|
});
|
|
});
|
|
/**
|
|
* Ready-made handler for Next's `app/robots.ts`. Re-export directly:
|
|
*
|
|
* ```ts
|
|
* // app/robots.ts
|
|
* export { robots as default } from '@/lib/content'
|
|
* ```
|
|
*/ const robots = ()=>buildRobots({
|
|
baseUrl: origin
|
|
});
|
|
return {
|
|
getCachedPayload,
|
|
getConfiguredLocales,
|
|
getEntries,
|
|
getSettings,
|
|
resolveRoute,
|
|
robots,
|
|
sitemap
|
|
};
|
|
}
|
|
|
|
//# sourceMappingURL=createContentHelpers.js.map
|