add dist
This commit is contained in:
+13
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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"}
|
||||
Vendored
+8
@@ -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';
|
||||
Vendored
+7
@@ -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
|
||||
Vendored
+1
@@ -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"}
|
||||
Vendored
+35
@@ -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 {};
|
||||
Vendored
+28
@@ -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
|
||||
Vendored
+1
@@ -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
@@ -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 {};
|
||||
Vendored
+111
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
Vendored
+29
@@ -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;
|
||||
Vendored
+17
@@ -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
|
||||
Vendored
+1
@@ -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"}
|
||||
Reference in New Issue
Block a user