This commit is contained in:
2026-07-31 23:21:51 +02:00
parent f6b4b36df8
commit f14f2cfd98
294 changed files with 5880 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
import type { Field } from 'payload';
import type { ContentOption } from './types.js';
/**
* Builds one relationship field per content collection, pointing at the
* client's Pages collection.
*
* Sits alongside the system-page roles (homepage, privacy policy) because it's
* the same idea: a page referenced by function rather than by slug. The
* assignment is locale-agnostic — one page document — while the resulting path
* is locale-aware, since the page's slug is localized. Assign the "Artykuły"
* page once and you get /pl/artykuly and /en/articles from its own slugs.
*/
export declare function buildArchiveFields(content: ContentOption, pagesSlug: string): Field[];
+27
View File
@@ -0,0 +1,27 @@
import { archiveFieldName } from './types.js';
/**
* Builds one relationship field per content collection, pointing at the
* client's Pages collection.
*
* Sits alongside the system-page roles (homepage, privacy policy) because it's
* the same idea: a page referenced by function rather than by slug. The
* assignment is locale-agnostic — one page document — while the resulting path
* is locale-aware, since the page's slug is localized. Assign the "Artykuły"
* page once and you get /pl/artykuly and /en/articles from its own slugs.
*/ export function buildArchiveFields(content, pagesSlug) {
return content.collections.map((collection)=>{
const label = collection.label ?? collection.slug;
return {
name: archiveFieldName(collection.slug),
type: 'relationship',
admin: {
description: `Page listing ${label}. Its slug becomes the URL segment for entries (e.g. /pl/artykuly/moj-wpis).`
},
label: `${label} — archive page`,
maxDepth: 1,
relationTo: pagesSlug
};
});
}
//# sourceMappingURL=archiveFields.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/modules/content/archiveFields.ts"],"sourcesContent":["import type { Field, RelationshipField } from 'payload'\n\nimport type { ContentOption } from './types.js'\n\nimport { archiveFieldName } from './types.js'\n\n/**\n * Builds one relationship field per content collection, pointing at the\n * client's Pages collection.\n *\n * Sits alongside the system-page roles (homepage, privacy policy) because it's\n * the same idea: a page referenced by function rather than by slug. The\n * assignment is locale-agnostic — one page document — while the resulting path\n * is locale-aware, since the page's slug is localized. Assign the \"Artykuły\"\n * page once and you get /pl/artykuly and /en/articles from its own slugs.\n */\nexport function buildArchiveFields(content: ContentOption, pagesSlug: string): Field[] {\n return content.collections.map((collection): RelationshipField => {\n const label = collection.label ?? collection.slug\n\n return {\n name: archiveFieldName(collection.slug),\n type: 'relationship',\n admin: {\n description: `Page listing ${label}. Its slug becomes the URL segment for entries (e.g. /pl/artykuly/moj-wpis).`,\n },\n label: `${label} — archive page`,\n maxDepth: 1,\n relationTo: pagesSlug,\n }\n })\n}\n"],"names":["archiveFieldName","buildArchiveFields","content","pagesSlug","collections","map","collection","label","slug","name","type","admin","description","maxDepth","relationTo"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,aAAY;AAE7C;;;;;;;;;CASC,GACD,OAAO,SAASC,mBAAmBC,OAAsB,EAAEC,SAAiB;IAC1E,OAAOD,QAAQE,WAAW,CAACC,GAAG,CAAC,CAACC;QAC9B,MAAMC,QAAQD,WAAWC,KAAK,IAAID,WAAWE,IAAI;QAEjD,OAAO;YACLC,MAAMT,iBAAiBM,WAAWE,IAAI;YACtCE,MAAM;YACNC,OAAO;gBACLC,aAAa,CAAC,aAAa,EAAEL,MAAM,4EAA4E,CAAC;YAClH;YACAA,OAAO,GAAGA,MAAM,eAAe,CAAC;YAChCM,UAAU;YACVC,YAAYX;QACd;IACF;AACF"}
+39
View File
@@ -0,0 +1,39 @@
import type { BasePayload } from 'payload';
export type ArchiveEntries<T = Record<string, unknown>> = {
docs: T[];
hasNextPage: boolean;
hasPrevPage: boolean;
/** 1-based. */
page: number;
totalDocs: number;
totalPages: number;
};
type GetArchiveEntriesArgs = {
/** Collection to list, e.g. 'posts'. */
collection: string;
/** Relationship depth. Defaults to 1 — enough for a cover image. */
depth?: number;
locale: string;
/** 1-based. Values below 1 are clamped. */
page?: number;
payload: BasePayload;
/** Defaults to 10. */
perPage?: number;
/** Payload sort string. Defaults to newest first. */
sort?: string;
/** Extra constraints merged into the query, e.g. { category: { equals: id } }. */
where?: Record<string, unknown>;
};
/**
* Fetches one page of a collection's entries for an archive listing.
*
* Only published documents are returned when the collection has drafts enabled;
* Payload's `where` on `_status` is left to the caller, since a collection
* without drafts has no such field.
*
* Returns pagination facts rather than markup — the listing itself belongs to
* the client (as a block on the archive page, most likely), because how a list
* of posts should look isn't something a plugin can decide.
*/
export declare function getArchiveEntries<T = Record<string, unknown>>({ collection, depth, locale, page, payload, perPage, sort, where, }: GetArchiveEntriesArgs): Promise<ArchiveEntries<T>>;
export {};
+33
View File
@@ -0,0 +1,33 @@
/**
* Fetches one page of a collection's entries for an archive listing.
*
* Only published documents are returned when the collection has drafts enabled;
* Payload's `where` on `_status` is left to the caller, since a collection
* without drafts has no such field.
*
* Returns pagination facts rather than markup — the listing itself belongs to
* the client (as a block on the archive page, most likely), because how a list
* of posts should look isn't something a plugin can decide.
*/ export async function getArchiveEntries({ collection, depth = 1, locale, page = 1, payload, perPage = 10, sort = '-createdAt', where }) {
const result = await payload.find({
collection: collection,
depth,
limit: perPage,
locale: locale,
page: Math.max(1, page),
sort,
...where ? {
where: where
} : {}
});
return {
docs: result.docs,
hasNextPage: result.hasNextPage,
hasPrevPage: result.hasPrevPage,
page: result.page ?? 1,
totalDocs: result.totalDocs,
totalPages: result.totalPages
};
}
//# sourceMappingURL=getArchiveEntries.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/modules/content/getArchiveEntries.ts"],"sourcesContent":["import type { BasePayload } from 'payload'\n\nexport type ArchiveEntries<T = Record<string, unknown>> = {\n docs: T[]\n hasNextPage: boolean\n hasPrevPage: boolean\n /** 1-based. */\n page: number\n totalDocs: number\n totalPages: number\n}\n\ntype GetArchiveEntriesArgs = {\n /** Collection to list, e.g. 'posts'. */\n collection: string\n /** Relationship depth. Defaults to 1 — enough for a cover image. */\n depth?: number\n locale: string\n /** 1-based. Values below 1 are clamped. */\n page?: number\n payload: BasePayload\n /** Defaults to 10. */\n perPage?: number\n /** Payload sort string. Defaults to newest first. */\n sort?: string\n /** Extra constraints merged into the query, e.g. { category: { equals: id } }. */\n where?: Record<string, unknown>\n}\n\n/**\n * Fetches one page of a collection's entries for an archive listing.\n *\n * Only published documents are returned when the collection has drafts enabled;\n * Payload's `where` on `_status` is left to the caller, since a collection\n * without drafts has no such field.\n *\n * Returns pagination facts rather than markup — the listing itself belongs to\n * the client (as a block on the archive page, most likely), because how a list\n * of posts should look isn't something a plugin can decide.\n */\nexport async function getArchiveEntries<T = Record<string, unknown>>({\n collection,\n depth = 1,\n locale,\n page = 1,\n payload,\n perPage = 10,\n sort = '-createdAt',\n where,\n}: GetArchiveEntriesArgs): Promise<ArchiveEntries<T>> {\n const result = await payload.find({\n collection: collection as never,\n depth,\n limit: perPage,\n locale: locale as never,\n page: Math.max(1, page),\n sort,\n ...(where ? { where: where as never } : {}),\n })\n\n return {\n docs: result.docs as T[],\n hasNextPage: result.hasNextPage,\n hasPrevPage: result.hasPrevPage,\n page: result.page ?? 1,\n totalDocs: result.totalDocs,\n totalPages: result.totalPages,\n }\n}\n"],"names":["getArchiveEntries","collection","depth","locale","page","payload","perPage","sort","where","result","find","limit","Math","max","docs","hasNextPage","hasPrevPage","totalDocs","totalPages"],"mappings":"AA6BA;;;;;;;;;;CAUC,GACD,OAAO,eAAeA,kBAA+C,EACnEC,UAAU,EACVC,QAAQ,CAAC,EACTC,MAAM,EACNC,OAAO,CAAC,EACRC,OAAO,EACPC,UAAU,EAAE,EACZC,OAAO,YAAY,EACnBC,KAAK,EACiB;IACtB,MAAMC,SAAS,MAAMJ,QAAQK,IAAI,CAAC;QAChCT,YAAYA;QACZC;QACAS,OAAOL;QACPH,QAAQA;QACRC,MAAMQ,KAAKC,GAAG,CAAC,GAAGT;QAClBG;QACA,GAAIC,QAAQ;YAAEA,OAAOA;QAAe,IAAI,CAAC,CAAC;IAC5C;IAEA,OAAO;QACLM,MAAML,OAAOK,IAAI;QACjBC,aAAaN,OAAOM,WAAW;QAC/BC,aAAaP,OAAOO,WAAW;QAC/BZ,MAAMK,OAAOL,IAAI,IAAI;QACrBa,WAAWR,OAAOQ,SAAS;QAC3BC,YAAYT,OAAOS,UAAU;IAC/B;AACF"}
+8
View File
@@ -0,0 +1,8 @@
export { buildArchiveFields } from './archiveFields.js';
export { getArchiveEntries } from './getArchiveEntries.js';
export type { ArchiveEntries } from './getArchiveEntries.js';
export { buildArchivePath, buildEntryPath, parsePageParam } from './paths.js';
export { resolveRoute } from './resolveRoute.js';
export type { ResolvedRoute } from './resolveRoute.js';
export type { ContentCollectionOption, ContentOption } from './types.js';
export { archiveFieldName } from './types.js';
+7
View File
@@ -0,0 +1,7 @@
export { buildArchiveFields } from './archiveFields.js';
export { getArchiveEntries } from './getArchiveEntries.js';
export { buildArchivePath, buildEntryPath, parsePageParam } from './paths.js';
export { resolveRoute } from './resolveRoute.js';
export { archiveFieldName } from './types.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/modules/content/index.ts"],"sourcesContent":["export { buildArchiveFields } from './archiveFields.js'\nexport { getArchiveEntries } from './getArchiveEntries.js'\nexport type { ArchiveEntries } from './getArchiveEntries.js'\nexport { buildArchivePath, buildEntryPath, parsePageParam } from './paths.js'\nexport { resolveRoute } from './resolveRoute.js'\nexport type { ResolvedRoute } from './resolveRoute.js'\nexport type { ContentCollectionOption, ContentOption } from './types.js'\nexport { archiveFieldName } from './types.js'\n"],"names":["buildArchiveFields","getArchiveEntries","buildArchivePath","buildEntryPath","parsePageParam","resolveRoute","archiveFieldName"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,qBAAoB;AACvD,SAASC,iBAAiB,QAAQ,yBAAwB;AAE1D,SAASC,gBAAgB,EAAEC,cAAc,EAAEC,cAAc,QAAQ,aAAY;AAC7E,SAASC,YAAY,QAAQ,oBAAmB;AAGhD,SAASC,gBAAgB,QAAQ,aAAY"}
+35
View File
@@ -0,0 +1,35 @@
type ArchivePathArgs = {
/** Archive page's slug in this locale, e.g. 'artykuly'. */
archiveSlug: string;
locale: string;
/** 1-based. Page 1 is omitted from the URL. */
page?: number;
};
/**
* Path to an archive listing: /pl/artykuly, /pl/artykuly?page=2.
*
* Pagination goes in the query string rather than the path. A path segment
* (/pl/artykuly/2) would be ambiguous with an entry slugged "2", and the
* alternative (/pl/artykuly/strona/2) drags in yet another localized segment to
* configure and translate. Google has understood ?page= for years, and
* rel=next/prev — the reason people used to prefer path segments — was retired.
*/
export declare function buildArchivePath({ archiveSlug, locale, page }: ArchivePathArgs): string;
type EntryPathArgs = {
/** Archive page's slug in this locale, e.g. 'artykuly'. */
archiveSlug: string;
/** Entry's slug in this locale, e.g. 'moj-post'. */
entrySlug: string;
locale: string;
};
/** Path to a single entry: /pl/artykuly/moj-post. */
export declare function buildEntryPath({ archiveSlug, entrySlug, locale }: EntryPathArgs): string;
/**
* Reads a page number out of a query parameter.
*
* Anything that isn't a positive integer is page 1 — `?page=abc`, `?page=-5`,
* `?page=1.5` and a repeated `?page=1&page=2` all have to resolve to something,
* and silently showing the first page beats a 500 on a URL a crawler invented.
*/
export declare function parsePageParam(value: string | string[] | undefined): number;
export {};
+28
View File
@@ -0,0 +1,28 @@
/**
* Path to an archive listing: /pl/artykuly, /pl/artykuly?page=2.
*
* Pagination goes in the query string rather than the path. A path segment
* (/pl/artykuly/2) would be ambiguous with an entry slugged "2", and the
* alternative (/pl/artykuly/strona/2) drags in yet another localized segment to
* configure and translate. Google has understood ?page= for years, and
* rel=next/prev — the reason people used to prefer path segments — was retired.
*/ export function buildArchivePath({ archiveSlug, locale, page = 1 }) {
const base = `/${locale}/${archiveSlug}`;
return page > 1 ? `${base}?page=${page}` : base;
}
/** Path to a single entry: /pl/artykuly/moj-post. */ export function buildEntryPath({ archiveSlug, entrySlug, locale }) {
return `/${locale}/${archiveSlug}/${entrySlug}`;
}
/**
* Reads a page number out of a query parameter.
*
* Anything that isn't a positive integer is page 1 — `?page=abc`, `?page=-5`,
* `?page=1.5` and a repeated `?page=1&page=2` all have to resolve to something,
* and silently showing the first page beats a 500 on a URL a crawler invented.
*/ export function parsePageParam(value) {
const raw = Array.isArray(value) ? value[0] : value;
const n = Number(raw);
return Number.isInteger(n) && n > 0 ? n : 1;
}
//# sourceMappingURL=paths.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/modules/content/paths.ts"],"sourcesContent":["type ArchivePathArgs = {\n /** Archive page's slug in this locale, e.g. 'artykuly'. */\n archiveSlug: string\n locale: string\n /** 1-based. Page 1 is omitted from the URL. */\n page?: number\n}\n\n/**\n * Path to an archive listing: /pl/artykuly, /pl/artykuly?page=2.\n *\n * Pagination goes in the query string rather than the path. A path segment\n * (/pl/artykuly/2) would be ambiguous with an entry slugged \"2\", and the\n * alternative (/pl/artykuly/strona/2) drags in yet another localized segment to\n * configure and translate. Google has understood ?page= for years, and\n * rel=next/prev — the reason people used to prefer path segments — was retired.\n */\nexport function buildArchivePath({ archiveSlug, locale, page = 1 }: ArchivePathArgs): string {\n const base = `/${locale}/${archiveSlug}`\n return page > 1 ? `${base}?page=${page}` : base\n}\n\ntype EntryPathArgs = {\n /** Archive page's slug in this locale, e.g. 'artykuly'. */\n archiveSlug: string\n /** Entry's slug in this locale, e.g. 'moj-post'. */\n entrySlug: string\n locale: string\n}\n\n/** Path to a single entry: /pl/artykuly/moj-post. */\nexport function buildEntryPath({ archiveSlug, entrySlug, locale }: EntryPathArgs): string {\n return `/${locale}/${archiveSlug}/${entrySlug}`\n}\n\n/**\n * Reads a page number out of a query parameter.\n *\n * Anything that isn't a positive integer is page 1 — `?page=abc`, `?page=-5`,\n * `?page=1.5` and a repeated `?page=1&page=2` all have to resolve to something,\n * and silently showing the first page beats a 500 on a URL a crawler invented.\n */\nexport function parsePageParam(value: string | string[] | undefined): number {\n const raw = Array.isArray(value) ? value[0] : value\n const n = Number(raw)\n return Number.isInteger(n) && n > 0 ? n : 1\n}\n"],"names":["buildArchivePath","archiveSlug","locale","page","base","buildEntryPath","entrySlug","parsePageParam","value","raw","Array","isArray","n","Number","isInteger"],"mappings":"AAQA;;;;;;;;CAQC,GACD,OAAO,SAASA,iBAAiB,EAAEC,WAAW,EAAEC,MAAM,EAAEC,OAAO,CAAC,EAAmB;IACjF,MAAMC,OAAO,CAAC,CAAC,EAAEF,OAAO,CAAC,EAAED,aAAa;IACxC,OAAOE,OAAO,IAAI,GAAGC,KAAK,MAAM,EAAED,MAAM,GAAGC;AAC7C;AAUA,mDAAmD,GACnD,OAAO,SAASC,eAAe,EAAEJ,WAAW,EAAEK,SAAS,EAAEJ,MAAM,EAAiB;IAC9E,OAAO,CAAC,CAAC,EAAEA,OAAO,CAAC,EAAED,YAAY,CAAC,EAAEK,WAAW;AACjD;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,eAAeC,KAAoC;IACjE,MAAMC,MAAMC,MAAMC,OAAO,CAACH,SAASA,KAAK,CAAC,EAAE,GAAGA;IAC9C,MAAMI,IAAIC,OAAOJ;IACjB,OAAOI,OAAOC,SAAS,CAACF,MAAMA,IAAI,IAAIA,IAAI;AAC5C"}
+68
View File
@@ -0,0 +1,68 @@
import type { BasePayload } from 'payload';
import type { ContentOption } from './types.js';
export type ResolvedRoute =
/** Locale root — the page assigned as Homepage in System Pages. */
{
/** The archive page this entry lives under — its slug is the URL prefix. */
archive: Record<string, unknown>;
collection: string;
doc: Record<string, unknown>;
type: 'entry';
}
/** An ordinary page. */
| {
collection: string;
doc: Record<string, unknown>;
/** 1-based, from ?page=. */
page: number;
perPage: number;
type: 'archive';
}
/** A collection's archive page, e.g. /pl/artykuly. */
| {
doc: Record<string, unknown>;
type: 'home';
}
/** A single entry, e.g. /pl/artykuly/moj-post. */
| {
doc: Record<string, unknown>;
type: 'page';
};
type ResolveRouteArgs = {
content?: ContentOption;
locale: string;
/** Page number from the query string (?page=2). Defaults to 1. */
page?: number;
/** Client's Pages collection slug. Defaults to 'pages'. */
pagesSlug?: string;
payload: BasePayload;
/** Route segments after the locale, e.g. ['artykuly', 'moj-post']. */
segments?: string[];
/** SiteSettings global slug. Defaults to 'site-settings'. */
settingsSlug?: string;
};
/**
* Works out what a URL points at: the home page, an ordinary page, a
* collection's archive, or a single entry.
*
* Routing only. An archive result says which collection to list and on which
* page, but doesn't fetch the entries — that's `getArchiveEntries`, called by
* whoever actually needs them. Keeping the two apart means listing changes
* (sorting, filtering, pagination) never touch routing rules, and metadata
* generation doesn't pay for a query it would discard.
*
* The trick is that archive prefixes aren't configured anywhere — they're the
* slug of whichever page an editor assigned as that collection's archive. So
* /pl/artykuly and /en/articles come from one assignment, and renaming the page
* moves the whole section.
*
* Resolution order matters: a first segment matching an archive's slug wins
* over an ordinary page of the same name, because an archive *is* a page and
* would otherwise shadow its own entries.
*
* Depth beyond {archive}/{entry} isn't supported — categories in the path would
* make canonical and hreflang ambiguous (the same entry reachable under several
* URLs).
*/
export declare function resolveRoute({ content, locale, page, pagesSlug, payload, segments, settingsSlug, }: ResolveRouteArgs): Promise<null | ResolvedRoute>;
export {};
+111
View File
@@ -0,0 +1,111 @@
import { archiveFieldName } from './types.js';
/**
* Works out what a URL points at: the home page, an ordinary page, a
* collection's archive, or a single entry.
*
* Routing only. An archive result says which collection to list and on which
* page, but doesn't fetch the entries — that's `getArchiveEntries`, called by
* whoever actually needs them. Keeping the two apart means listing changes
* (sorting, filtering, pagination) never touch routing rules, and metadata
* generation doesn't pay for a query it would discard.
*
* The trick is that archive prefixes aren't configured anywhere — they're the
* slug of whichever page an editor assigned as that collection's archive. So
* /pl/artykuly and /en/articles come from one assignment, and renaming the page
* moves the whole section.
*
* Resolution order matters: a first segment matching an archive's slug wins
* over an ordinary page of the same name, because an archive *is* a page and
* would otherwise shadow its own entries.
*
* Depth beyond {archive}/{entry} isn't supported — categories in the path would
* make canonical and hreflang ambiguous (the same entry reachable under several
* URLs).
*/ export async function resolveRoute({ content, locale, page = 1, pagesSlug = 'pages', payload, segments, settingsSlug = 'site-settings' }) {
const settings = await payload.findGlobal({
slug: settingsSlug,
depth: 1,
locale: locale
});
// Locale root → Homepage from System Pages.
if (!segments?.length) {
const homepage = settings.homepage;
if (homepage && typeof homepage === 'object') {
return {
type: 'home',
doc: homepage
};
}
return null;
}
const [first, ...rest] = segments;
// Does the first segment name an archive page?
for (const collection of content?.collections ?? []){
const archive = settings[archiveFieldName(collection.slug)];
if (!archive || typeof archive !== 'object') {
continue;
}
const archiveDoc = archive;
if (archiveDoc.slug !== first) {
continue;
}
// /pl/artykuly → the archive page itself.
if (rest.length === 0) {
return {
type: 'archive',
collection: collection.slug,
doc: archiveDoc,
page,
perPage: collection.perPage ?? 10
};
}
// /pl/artykuly/moj-post → an entry.
if (rest.length === 1) {
const found = await payload.find({
collection: collection.slug,
depth: 2,
limit: 1,
locale: locale,
where: {
slug: {
equals: rest[0]
}
}
});
const entry = found.docs[0];
if (!entry) {
return null;
}
return {
type: 'entry',
archive: archiveDoc,
collection: collection.slug,
doc: entry
};
}
// Deeper than {archive}/{entry}.
return null;
}
// An ordinary page. Joined, so nested slugs ('a/b') keep working.
const found = await payload.find({
collection: pagesSlug,
depth: 2,
limit: 1,
locale: locale,
where: {
slug: {
equals: segments.join('/')
}
}
});
const doc = found.docs[0];
if (!doc) {
return null;
}
return {
type: 'page',
doc: doc
};
}
//# sourceMappingURL=resolveRoute.js.map
File diff suppressed because one or more lines are too long
+29
View File
@@ -0,0 +1,29 @@
/**
* A collection whose entries live under an archive page, e.g. blog posts at
* /pl/artykuly/moj-post.
*
* The collection itself belongs to the client — Payload keeps its schema in
* code, so adding one is always a code change. What this option adds is the
* routing: the plugin exposes an "archive page" assignment in SiteSettings, and
* whichever page an editor picks becomes the URL segment. Rename that page from
* "Artykuły" to "Wpisy" and the path follows, per locale, with no deploy.
*/
export type ContentCollectionOption = {
/** Slug of the client's collection, e.g. 'posts'. */
slug: string;
/** Admin label for the archive assignment. Defaults to the slug. */
label?: string;
/** Entries per page in listings. Defaults to 10. */
perPage?: number;
};
/**
* Plugin option wiring archive-backed collections into routing.
*/
export type ContentOption = {
collections: ContentCollectionOption[];
};
/**
* Field name holding a collection's archive page in SiteSettings.
* `posts` → `postsArchive`.
*/
export declare function archiveFieldName(collectionSlug: string): string;
+17
View File
@@ -0,0 +1,17 @@
/**
* A collection whose entries live under an archive page, e.g. blog posts at
* /pl/artykuly/moj-post.
*
* The collection itself belongs to the client — Payload keeps its schema in
* code, so adding one is always a code change. What this option adds is the
* routing: the plugin exposes an "archive page" assignment in SiteSettings, and
* whichever page an editor picks becomes the URL segment. Rename that page from
* "Artykuły" to "Wpisy" and the path follows, per locale, with no deploy.
*/ /**
* Field name holding a collection's archive page in SiteSettings.
* `posts` → `postsArchive`.
*/ export function archiveFieldName(collectionSlug) {
return `${collectionSlug}Archive`;
}
//# sourceMappingURL=types.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/modules/content/types.ts"],"sourcesContent":["/**\n * A collection whose entries live under an archive page, e.g. blog posts at\n * /pl/artykuly/moj-post.\n *\n * The collection itself belongs to the client — Payload keeps its schema in\n * code, so adding one is always a code change. What this option adds is the\n * routing: the plugin exposes an \"archive page\" assignment in SiteSettings, and\n * whichever page an editor picks becomes the URL segment. Rename that page from\n * \"Artykuły\" to \"Wpisy\" and the path follows, per locale, with no deploy.\n */\nexport type ContentCollectionOption = {\n /** Slug of the client's collection, e.g. 'posts'. */\n slug: string\n /** Admin label for the archive assignment. Defaults to the slug. */\n label?: string\n /** Entries per page in listings. Defaults to 10. */\n perPage?: number\n}\n\n/**\n * Plugin option wiring archive-backed collections into routing.\n */\nexport type ContentOption = {\n collections: ContentCollectionOption[]\n}\n\n/**\n * Field name holding a collection's archive page in SiteSettings.\n * `posts` → `postsArchive`.\n */\nexport function archiveFieldName(collectionSlug: string): string {\n return `${collectionSlug}Archive`\n}\n"],"names":["archiveFieldName","collectionSlug"],"mappings":"AAAA;;;;;;;;;CASC,GAiBD;;;CAGC,GACD,OAAO,SAASA,iBAAiBC,cAAsB;IACrD,OAAO,GAAGA,eAAe,OAAO,CAAC;AACnC"}