@justanarthur/payload-www 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -124,6 +124,11 @@ done. `export const instant = false` tells Next's dev-time instant validation th
124
124
  blocks on purpose. With `partialPrefetching: true`, Next streams the layout shell for slugs it did
125
125
  not prerender, so those slugs still get a soft 404.
126
126
 
127
+ `prerenderLocales` limits which locales' documents `generateStaticParams` prerenders at build;
128
+ the rest render on their first request and are cached from there. A large multilingual
129
+ collection can prerender only its main locale, e.g. `prerenderLocales: ['en']` for posts, to keep
130
+ the build short. It defaults to every locale.
131
+
127
132
  `slugShape` is `'single'` (default) or `'catch-all'`. Use `'catch-all'` if your route segment is
128
133
  `[[...slug]]` (Pages-style), `'single'` for `[slug]` (Posts-style — file convention).
129
134
 
@@ -305,7 +310,8 @@ scopes. Setup:
305
310
 
306
311
  6. **List with `queryDocs`, never a raw `payload.find`** — a read inside `'use cache'` that
307
312
  doesn't go through the finders carries no tags, so nothing the admin does can bust it.
308
- `queryDocs` tags list membership plus every returned doc and what is populated into it:
313
+ `queryDocs` tags a per-collection `content` list plus every returned doc and what is
314
+ populated into it:
309
315
 
310
316
  ```ts
311
317
  import { queryDocs } from '@justanarthur/payload-www/metadata'
@@ -315,21 +321,16 @@ scopes. Setup:
315
321
  locale,
316
322
  sort: '-publishedAt',
317
323
  limit: 10,
318
- depth: 1,
319
- list: 'archive'
324
+ depth: 1
320
325
  })
321
326
  ```
322
327
 
323
- `list` names a scope whose sort and filter fields are declared on the plugin, so moving a
324
- post's date or category reorders the archive even when that post wasn't on the page:
325
-
326
- ```ts
327
- createWWWConfig().withWWWConfig({
328
- defaultPluginsConfigs: {
329
- revalidate: { collections: { posts: { lists: { archive: ['publishedAt', 'categories'] } } } }
330
- }
331
- })
332
- ```
328
+ `createWWWConfig` declares that `content` scope on every collection over all its top-level
329
+ fields, so creating, publishing, unpublishing, deleting or really changing any doc refreshes
330
+ every listing of its collection — a moved date or category reorders an archive even when that
331
+ post wasn't on the page. Saves that change nothing and draft saves leave it alone. For a
332
+ narrower listing, declare a scope through `defaultPluginsConfigs.revalidate` and pass it as
333
+ `list`.
333
334
 
334
335
  The cached profile is `cacheLife('weeks')` — Next 16's built-in long-tail profile (5 m stale,
335
336
  1 w revalidate, 30 d expire). No custom `cacheLife` config entry needed. Invalidations are
@@ -111,6 +111,27 @@ function slugToPath(slug) {
111
111
  return slug.split(SLUG_NESTED_DIVIDER).join("/");
112
112
  }
113
113
 
114
+ // src/revalidate/contentLists.ts
115
+ import { revalidatePlugin } from "@pro-laico/payload-revalidate";
116
+ import { flattenTopLevelFields } from "payload";
117
+ var CONTENT_LIST = "content";
118
+ function revalidateWithContentLists(options) {
119
+ return (config) => revalidatePlugin({
120
+ ...options,
121
+ collections: { ...options.collections, ...contentLists(config.collections ?? [], options.collections ?? {}) }
122
+ })(config);
123
+ }
124
+ function contentLists(collections, overrides) {
125
+ return Object.fromEntries(collections.flatMap(({ slug, fields, custom }) => {
126
+ const marker = custom?.revalidate;
127
+ const override = overrides[slug];
128
+ if (override === false || marker === false && override === undefined)
129
+ return [];
130
+ const lists = { [CONTENT_LIST]: flattenTopLevelFields(fields).map(({ name }) => name), ...marker?.lists, ...override?.lists };
131
+ return [[slug, { ...override, lists }]];
132
+ }));
133
+ }
134
+
114
135
  // src/render/metadata/query.ts
115
136
  var _helpers = null;
116
137
  var _seedPromise = null;
@@ -178,7 +199,7 @@ async function queryAllDocs(args) {
178
199
  }
179
200
  async function queryDocs({
180
201
  collectionSlug,
181
- list,
202
+ list = CONTENT_LIST,
182
203
  draft = false,
183
204
  depth = RENDER_DEPTH,
184
205
  ...find
package/dist/config.js CHANGED
@@ -313,7 +313,29 @@ import { seoPlugin } from "@justanarthur/payload-plugin-seo";
313
313
  import { imageHashPlugin } from "@justanarthur/payload-imagehash-plugin";
314
314
  import { translator } from "@justanarthur/payload-plugin-translator";
315
315
  import { mcpPlugin } from "@payloadcms/plugin-mcp";
316
+
317
+ // src/revalidate/contentLists.ts
316
318
  import { revalidatePlugin } from "@pro-laico/payload-revalidate";
319
+ import { flattenTopLevelFields } from "payload";
320
+ var CONTENT_LIST = "content";
321
+ function revalidateWithContentLists(options) {
322
+ return (config) => revalidatePlugin({
323
+ ...options,
324
+ collections: { ...options.collections, ...contentLists(config.collections ?? [], options.collections ?? {}) }
325
+ })(config);
326
+ }
327
+ function contentLists(collections, overrides) {
328
+ return Object.fromEntries(collections.flatMap(({ slug, fields, custom }) => {
329
+ const marker = custom?.revalidate;
330
+ const override = overrides[slug];
331
+ if (override === false || marker === false && override === undefined)
332
+ return [];
333
+ const lists = { [CONTENT_LIST]: flattenTopLevelFields(fields).map(({ name }) => name), ...marker?.lists, ...override?.lists };
334
+ return [[slug, { ...override, lists }]];
335
+ }));
336
+ }
337
+
338
+ // src/createWWWConfig.ts
317
339
  function createWWWConfig() {
318
340
  function withWWWConfig(input) {
319
341
  const { defaultPluginsConfigs, ...config } = input;
@@ -363,7 +385,7 @@ function createWWWConfig() {
363
385
  ];
364
386
  const plugins = [
365
387
  ...mergeOrOverride(defaultPlugins, config.plugins),
366
- revalidatePlugin(revalidate === false ? { enabled: false } : mergeOrOverride({}, revalidate))
388
+ revalidateWithContentLists(revalidate === false ? { enabled: false } : mergeOrOverride({}, revalidate))
367
389
  ];
368
390
  return {
369
391
  ...config,
package/dist/metadata.js CHANGED
@@ -120,6 +120,29 @@ import { createCacheHelpers } from "@pro-laico/payload-revalidate/cache";
120
120
  import { tagsFor } from "@pro-laico/payload-revalidate";
121
121
  import { cacheLife } from "next/cache";
122
122
  import { getPayload } from "payload";
123
+
124
+ // src/revalidate/contentLists.ts
125
+ import { revalidatePlugin } from "@pro-laico/payload-revalidate";
126
+ import { flattenTopLevelFields } from "payload";
127
+ var CONTENT_LIST = "content";
128
+ function revalidateWithContentLists(options) {
129
+ return (config) => revalidatePlugin({
130
+ ...options,
131
+ collections: { ...options.collections, ...contentLists(config.collections ?? [], options.collections ?? {}) }
132
+ })(config);
133
+ }
134
+ function contentLists(collections, overrides) {
135
+ return Object.fromEntries(collections.flatMap(({ slug, fields, custom }) => {
136
+ const marker = custom?.revalidate;
137
+ const override = overrides[slug];
138
+ if (override === false || marker === false && override === undefined)
139
+ return [];
140
+ const lists = { [CONTENT_LIST]: flattenTopLevelFields(fields).map(({ name }) => name), ...marker?.lists, ...override?.lists };
141
+ return [[slug, { ...override, lists }]];
142
+ }));
143
+ }
144
+
145
+ // src/render/metadata/query.ts
123
146
  var _helpers = null;
124
147
  var _seedPromise = null;
125
148
  function seedPayloadCache({ config }) {
@@ -186,7 +209,7 @@ async function queryAllDocs(args) {
186
209
  }
187
210
  async function queryDocs({
188
211
  collectionSlug,
189
- list,
212
+ list = CONTENT_LIST,
190
213
  draft = false,
191
214
  depth = RENDER_DEPTH,
192
215
  ...find
package/dist/pages.d.ts CHANGED
@@ -41,6 +41,8 @@ type CreateCollectionPageExportsArgs<S extends string = "pages"> = {
41
41
  slugShape?: SlugShape;
42
42
  /** Relationship / upload hops populated on the rendered document. `0` renders them as bare ids. */
43
43
  depth?: number;
44
+ /** Locales whose documents prerender at build. The rest render on their first request and cache from there. Defaults to every locale. */
45
+ prerenderLocales?: string[];
44
46
  };
45
47
  type CreateCollectionPageExportsDeps<S extends string> = {
46
48
  getServerSideURL: () => string;
@@ -48,7 +50,7 @@ type CreateCollectionPageExportsDeps<S extends string> = {
48
50
  /** Rendered while the document streams. Defaults to nothing. */
49
51
  fallback?: ReactNode2;
50
52
  };
51
- declare function createCollectionPageExports<S extends string = "pages">({ slug: collectionSlug, _payloadConfig, importMap, routing, slugShape, depth }: CreateCollectionPageExportsArgs<S>, { getServerSideURL, pagePathPrefix, fallback }: CreateCollectionPageExportsDeps<S>): {
53
+ declare function createCollectionPageExports<S extends string = "pages">({ slug: collectionSlug, _payloadConfig, importMap, routing, slugShape, depth, prerenderLocales }: CreateCollectionPageExportsArgs<S>, { getServerSideURL, pagePathPrefix, fallback }: CreateCollectionPageExportsDeps<S>): {
52
54
  default: (props: NextPageProps) => Promise<ReactNode2>;
53
55
  generateMetadata: (props: NextPageProps) => Promise<Metadata>;
54
56
  generateStaticParams: (props: GenerateStaticParamsProps) => Promise<{
package/dist/pages.js CHANGED
@@ -205,6 +205,29 @@ import { createCacheHelpers } from "@pro-laico/payload-revalidate/cache";
205
205
  import { tagsFor } from "@pro-laico/payload-revalidate";
206
206
  import { cacheLife } from "next/cache";
207
207
  import { getPayload } from "payload";
208
+
209
+ // src/revalidate/contentLists.ts
210
+ import { revalidatePlugin } from "@pro-laico/payload-revalidate";
211
+ import { flattenTopLevelFields } from "payload";
212
+ var CONTENT_LIST = "content";
213
+ function revalidateWithContentLists(options) {
214
+ return (config) => revalidatePlugin({
215
+ ...options,
216
+ collections: { ...options.collections, ...contentLists(config.collections ?? [], options.collections ?? {}) }
217
+ })(config);
218
+ }
219
+ function contentLists(collections, overrides) {
220
+ return Object.fromEntries(collections.flatMap(({ slug, fields, custom }) => {
221
+ const marker = custom?.revalidate;
222
+ const override = overrides[slug];
223
+ if (override === false || marker === false && override === undefined)
224
+ return [];
225
+ const lists = { [CONTENT_LIST]: flattenTopLevelFields(fields).map(({ name }) => name), ...marker?.lists, ...override?.lists };
226
+ return [[slug, { ...override, lists }]];
227
+ }));
228
+ }
229
+
230
+ // src/render/metadata/query.ts
208
231
  var _helpers = null;
209
232
  var _seedPromise = null;
210
233
  function seedPayloadCache({ config }) {
@@ -271,7 +294,7 @@ async function queryAllDocs(args) {
271
294
  }
272
295
  async function queryDocs({
273
296
  collectionSlug,
274
- list,
297
+ list = CONTENT_LIST,
275
298
  draft = false,
276
299
  depth = RENDER_DEPTH,
277
300
  ...find
@@ -392,7 +415,8 @@ function createCollectionPageExports({
392
415
  importMap,
393
416
  routing,
394
417
  slugShape = "single",
395
- depth = RENDER_DEPTH
418
+ depth = RENDER_DEPTH,
419
+ prerenderLocales = routing.locales
396
420
  }, {
397
421
  getServerSideURL,
398
422
  pagePathPrefix,
@@ -459,7 +483,7 @@ function createCollectionPageExports({
459
483
  }
460
484
  async function generateStaticParams(props) {
461
485
  await props.params;
462
- const perLocaleEntries = await Promise.all(routing.locales.map(async (locale) => {
486
+ const perLocaleEntries = await Promise.all(prerenderLocales.map(async (locale) => {
463
487
  const docs = await queryAllDocs({ locale, collectionSlug });
464
488
  const entries = docs.filter((doc) => !isIndexSlug(doc.slug)).map((doc) => ({ locale, slug: slugToParamsSlug(doc.slug, slugShape) }));
465
489
  if (shapeHasIndexPath(slugShape) && await fetchDoc(locale, ""))
@@ -50,6 +50,8 @@ type CreateCollectionPageExportsArgs<S extends string = "pages"> = {
50
50
  slugShape?: SlugShape;
51
51
  /** Relationship / upload hops populated on the rendered document. `0` renders them as bare ids. */
52
52
  depth?: number;
53
+ /** Locales whose documents prerender at build. The rest render on their first request and cache from there. Defaults to every locale. */
54
+ prerenderLocales?: string[];
53
55
  };
54
56
  type CreateCollectionPageExportsDeps<S extends string> = {
55
57
  getServerSideURL: () => string;
@@ -57,7 +59,7 @@ type CreateCollectionPageExportsDeps<S extends string> = {
57
59
  /** Rendered while the document streams. Defaults to nothing. */
58
60
  fallback?: ReactNode2;
59
61
  };
60
- declare function createCollectionPageExports<S extends string = "pages">({ slug: collectionSlug, _payloadConfig, importMap, routing, slugShape, depth }: CreateCollectionPageExportsArgs<S>, { getServerSideURL, pagePathPrefix, fallback }: CreateCollectionPageExportsDeps<S>): {
62
+ declare function createCollectionPageExports<S extends string = "pages">({ slug: collectionSlug, _payloadConfig, importMap, routing, slugShape, depth, prerenderLocales }: CreateCollectionPageExportsArgs<S>, { getServerSideURL, pagePathPrefix, fallback }: CreateCollectionPageExportsDeps<S>): {
61
63
  default: (props: NextPageProps) => Promise<ReactNode2>;
62
64
  generateMetadata: (props: NextPageProps) => Promise<Metadata>;
63
65
  generateStaticParams: (props: GenerateStaticParamsProps) => Promise<{
@@ -235,6 +235,29 @@ import { createCacheHelpers } from "@pro-laico/payload-revalidate/cache";
235
235
  import { tagsFor } from "@pro-laico/payload-revalidate";
236
236
  import { cacheLife } from "next/cache";
237
237
  import { getPayload } from "payload";
238
+
239
+ // src/revalidate/contentLists.ts
240
+ import { revalidatePlugin } from "@pro-laico/payload-revalidate";
241
+ import { flattenTopLevelFields } from "payload";
242
+ var CONTENT_LIST = "content";
243
+ function revalidateWithContentLists(options) {
244
+ return (config) => revalidatePlugin({
245
+ ...options,
246
+ collections: { ...options.collections, ...contentLists(config.collections ?? [], options.collections ?? {}) }
247
+ })(config);
248
+ }
249
+ function contentLists(collections, overrides) {
250
+ return Object.fromEntries(collections.flatMap(({ slug, fields, custom }) => {
251
+ const marker = custom?.revalidate;
252
+ const override = overrides[slug];
253
+ if (override === false || marker === false && override === undefined)
254
+ return [];
255
+ const lists = { [CONTENT_LIST]: flattenTopLevelFields(fields).map(({ name }) => name), ...marker?.lists, ...override?.lists };
256
+ return [[slug, { ...override, lists }]];
257
+ }));
258
+ }
259
+
260
+ // src/render/metadata/query.ts
238
261
  var _helpers = null;
239
262
  var _seedPromise = null;
240
263
  function seedPayloadCache({ config }) {
@@ -301,7 +324,7 @@ async function queryAllDocs(args) {
301
324
  }
302
325
  async function queryDocs({
303
326
  collectionSlug,
304
- list,
327
+ list = CONTENT_LIST,
305
328
  draft = false,
306
329
  depth = RENDER_DEPTH,
307
330
  ...find
@@ -422,7 +445,8 @@ function createCollectionPageExports({
422
445
  importMap,
423
446
  routing,
424
447
  slugShape = "single",
425
- depth = RENDER_DEPTH
448
+ depth = RENDER_DEPTH,
449
+ prerenderLocales = routing.locales
426
450
  }, {
427
451
  getServerSideURL,
428
452
  pagePathPrefix,
@@ -489,7 +513,7 @@ function createCollectionPageExports({
489
513
  }
490
514
  async function generateStaticParams(props) {
491
515
  await props.params;
492
- const perLocaleEntries = await Promise.all(routing.locales.map(async (locale) => {
516
+ const perLocaleEntries = await Promise.all(prerenderLocales.map(async (locale) => {
493
517
  const docs = await queryAllDocs({ locale, collectionSlug });
494
518
  const entries = docs.filter((doc) => !isIndexSlug(doc.slug)).map((doc) => ({ locale, slug: slugToParamsSlug(doc.slug, slugShape) }));
495
519
  if (shapeHasIndexPath(slugShape) && await fetchDoc(locale, ""))
package/dist/sitemap.js CHANGED
@@ -30,6 +30,29 @@ import { createCacheHelpers } from "@pro-laico/payload-revalidate/cache";
30
30
  import { tagsFor } from "@pro-laico/payload-revalidate";
31
31
  import { cacheLife } from "next/cache";
32
32
  import { getPayload } from "payload";
33
+
34
+ // src/revalidate/contentLists.ts
35
+ import { revalidatePlugin } from "@pro-laico/payload-revalidate";
36
+ import { flattenTopLevelFields } from "payload";
37
+ var CONTENT_LIST = "content";
38
+ function revalidateWithContentLists(options) {
39
+ return (config) => revalidatePlugin({
40
+ ...options,
41
+ collections: { ...options.collections, ...contentLists(config.collections ?? [], options.collections ?? {}) }
42
+ })(config);
43
+ }
44
+ function contentLists(collections, overrides) {
45
+ return Object.fromEntries(collections.flatMap(({ slug, fields, custom }) => {
46
+ const marker = custom?.revalidate;
47
+ const override = overrides[slug];
48
+ if (override === false || marker === false && override === undefined)
49
+ return [];
50
+ const lists = { [CONTENT_LIST]: flattenTopLevelFields(fields).map(({ name }) => name), ...marker?.lists, ...override?.lists };
51
+ return [[slug, { ...override, lists }]];
52
+ }));
53
+ }
54
+
55
+ // src/render/metadata/query.ts
33
56
  var _helpers = null;
34
57
  var _seedPromise = null;
35
58
  function seedPayloadCache({ config }) {
@@ -96,7 +119,7 @@ async function queryAllDocs(args) {
96
119
  }
97
120
  async function queryDocs({
98
121
  collectionSlug,
99
- list,
122
+ list = CONTENT_LIST,
100
123
  draft = false,
101
124
  depth = RENDER_DEPTH,
102
125
  ...find
@@ -238,7 +261,8 @@ function createCollectionPageExports({
238
261
  importMap,
239
262
  routing,
240
263
  slugShape = "single",
241
- depth = RENDER_DEPTH
264
+ depth = RENDER_DEPTH,
265
+ prerenderLocales = routing.locales
242
266
  }, {
243
267
  getServerSideURL,
244
268
  pagePathPrefix,
@@ -305,7 +329,7 @@ function createCollectionPageExports({
305
329
  }
306
330
  async function generateStaticParams(props) {
307
331
  await props.params;
308
- const perLocaleEntries = await Promise.all(routing.locales.map(async (locale) => {
332
+ const perLocaleEntries = await Promise.all(prerenderLocales.map(async (locale) => {
309
333
  const docs = await queryAllDocs({ locale, collectionSlug });
310
334
  const entries = docs.filter((doc) => !isIndexSlug(doc.slug)).map((doc) => ({ locale, slug: slugToParamsSlug(doc.slug, slugShape) }));
311
335
  if (shapeHasIndexPath(slugShape) && await fetchDoc(locale, ""))
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justanarthur/payload-www",
3
3
  "description": "Reusable Payload CMS website template — config builder, collections, globals, blocks, fields, access, hooks, metadata (JSON-LD, hreflang), page renderers, and test helpers.",
4
- "version": "2.3.0",
4
+ "version": "2.5.0",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "private": false,