@justanarthur/payload-www 1.2.1 → 1.3.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/dist/blocks.js CHANGED
@@ -32,7 +32,9 @@ var RenderBlocks = async ({ blocks, blockProps, config, importMap, locale, searc
32
32
  const block = blocks[i];
33
33
  const { blockType } = block;
34
34
  const blockConfig = (config.blocks ?? []).find((b) => b.slug === blockType);
35
- const customPath = blockConfig?.custom?.[name]?.path;
35
+ const blockCustom = blockConfig?.custom?.[name];
36
+ const customPath = blockCustom?.path;
37
+ const wantsSearchParams = blockCustom?.searchParams === true;
36
38
  const importMapPath = (typeof customPath === "string" ? customPath : null) ?? `${DEFAULT_BLOCK_PATH_PREFIX}/${blockType}`;
37
39
  const Block = await getFromImportMap(importMapPath, importMap);
38
40
  if (!Block) {
@@ -44,7 +46,7 @@ var RenderBlocks = async ({ blocks, blockProps, config, importMap, locale, searc
44
46
  ...blockProps,
45
47
  ...block,
46
48
  locale,
47
- searchParams
49
+ ...wantsSearchParams ? { searchParams } : null
48
50
  }, i));
49
51
  }
50
52
  return /* @__PURE__ */ jsx(Fragment, {
package/dist/cli.js CHANGED
@@ -80,41 +80,13 @@ async function generateAsyncImportmap(config, options) {
80
80
 
81
81
  // src/cli/index.ts
82
82
  register();
83
- function printHelp() {
84
- console.log(`payload-www generate:async-importmap
85
-
86
- Generate an async-importMap.ts containing only the render-path dependencies
87
- (blocks + collection/global renderers) used by the public render path.
88
-
89
- Usage:
90
- payload-www generate:async-importmap --config-path <path> --output <path>
91
-
92
- Options:
93
- --config-path <path> Path to the Payload config file (TS or JS).
94
- Resolved relative to the current working directory.
95
- --output <path> Output file path. Created if missing.
96
- --package-name <name> Custom-package key for the render-dependency lookup.
97
- Defaults to "@justanarthur/payload-www".
98
- --help, -h Show this help.
99
-
100
- Example:
101
- payload-www generate:async-importmap \\
102
- --config-path apps/www/payload.config.ts \\
103
- --output apps/www/app/\\(payload\\)/admin/asyncImportMap.ts
104
- `);
105
- }
106
83
  function parseArgs(argv) {
107
84
  const args = {
108
85
  configPath: "",
109
- output: "",
110
- help: false
86
+ output: ""
111
87
  };
112
88
  for (let i = 0;i < argv.length; i++) {
113
89
  const a = argv[i];
114
- if (a === "--help" || a === "-h") {
115
- args.help = true;
116
- continue;
117
- }
118
90
  if (a === "--config-path") {
119
91
  args.configPath = argv[++i] ?? "";
120
92
  continue;
@@ -144,14 +116,8 @@ function parseArgs(argv) {
144
116
  }
145
117
  async function main() {
146
118
  const args = parseArgs(process.argv.slice(2));
147
- if (args.help) {
148
- printHelp();
149
- process.exit(0);
150
- }
151
119
  if (!args.configPath || !args.output) {
152
- console.error(`[payload-www] --config-path and --output are required.
153
- `);
154
- printHelp();
120
+ console.error("[payload-www] --config-path and --output are required.");
155
121
  process.exit(1);
156
122
  }
157
123
  const cwd = process.cwd();
@@ -178,11 +144,10 @@ async function main() {
178
144
  const { writeFileSync } = await import("node:fs");
179
145
  writeFileSync("/tmp/payload-config-dump.json", JSON.stringify(config, (k, v) => typeof v === "function" ? "[function]" : v, 2).slice(0, 200000));
180
146
  }
181
- const result = await generateAsyncImportmap(config, {
147
+ await generateAsyncImportmap(config, {
182
148
  output: path.isAbsolute(args.output) ? args.output : path.resolve(cwd, args.output),
183
149
  ...args.packageName ? { packageName: args.packageName } : {}
184
150
  });
185
- console.log(`[payload-www] wrote ${result.entries} entries to ${result.output}`);
186
151
  }
187
152
  main().catch((err) => {
188
153
  console.error("[payload-www] unhandled error:");
@@ -177,16 +177,55 @@ async function queryAllDocs({
177
177
  }) {
178
178
  return withUnstableCache([collectionSlug, slugField2, locale], [createCollectionCacheKey({ collectionSlug, slug: "__all__", locale })], async () => {
179
179
  const payload = await getPayload({ config });
180
- const result = await payload.find({
181
- collection: collectionSlug,
182
- draft: false,
183
- limit: 1000,
184
- pagination: false,
185
- overrideAccess: false,
186
- select: { [slugField2]: true },
187
- locale
188
- });
189
- return result.docs ?? [];
180
+ const collectionConfig = payload.collections[collectionSlug]?.config;
181
+ if (!collectionConfig)
182
+ return [];
183
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
184
+ const snakeSlugField = slugField2;
185
+ const drizzleDb = payload.db.drizzle;
186
+ if (!drizzleDb?.execute) {
187
+ const result2 = await payload.find({
188
+ collection: collectionSlug,
189
+ draft: false,
190
+ limit: 1000,
191
+ pagination: false,
192
+ overrideAccess: true
193
+ });
194
+ return (result2.docs ?? []).map((d) => ({ [slugField2]: d[slugField2] }));
195
+ }
196
+ const hasVersions = Boolean(collectionConfig.versions);
197
+ const isSlugLocalized = (() => {
198
+ const fields = collectionConfig.fields;
199
+ if (!Array.isArray(fields))
200
+ return false;
201
+ const found = fields.find((f) => f.name === slugField2);
202
+ return Boolean(found?.localized);
203
+ })();
204
+ const hasLocales = Boolean(collectionConfig.custom?._isLocalized) || Boolean(payload.config.localization?.locales?.length);
205
+ const localesTableName = `${tableName}_locales`;
206
+ let sqlText;
207
+ const safeLocale = locale.replace(/'/g, "''");
208
+ if (isSlugLocalized && hasLocales) {
209
+ const statusFilter = hasVersions ? ` AND "${tableName}"."_status" = 'published'` : "";
210
+ sqlText = `
211
+ SELECT DISTINCT ON ("${tableName}"."id")
212
+ "${tableName}"."id" AS "id",
213
+ "${localesTableName}"."${snakeSlugField}" AS "${snakeSlugField}",
214
+ "${tableName}"."updated_at" AS "updated_at"
215
+ FROM "${tableName}"
216
+ JOIN "${localesTableName}" ON "${localesTableName}"."_parent_id" = "${tableName}"."id"
217
+ WHERE "${localesTableName}"."_locale" = '${safeLocale}'${statusFilter}
218
+ ORDER BY "${tableName}"."id", "${tableName}"."created_at" DESC
219
+ LIMIT 1000
220
+ `;
221
+ } else {
222
+ const statusFilter = hasVersions ? ` WHERE "_status" = 'published'` : "";
223
+ sqlText = `SELECT id, "${snakeSlugField}", "updated_at" FROM "${tableName}"${statusFilter} ORDER BY "created_at" DESC LIMIT 1000`;
224
+ }
225
+ const { sql: drizzleSql } = await import("drizzle-orm");
226
+ const result = await drizzleDb.execute(drizzleSql.raw(sqlText));
227
+ const rows = result.rows ?? [];
228
+ return rows.map((r) => ({ [slugField2]: r[snakeSlugField], id: r.id, updatedAt: r.updated_at }));
190
229
  });
191
230
  }
192
231
  async function queryAllLocaleSlugs({
@@ -196,13 +235,46 @@ async function queryAllLocaleSlugs({
196
235
  config
197
236
  }) {
198
237
  const payload = await getPayload({ config });
199
- const doc = await payload.findByID({
200
- collection: collectionSlug,
201
- id,
202
- locale: "all",
203
- select: { [slugField2]: true }
204
- });
205
- return doc?.[slugField2];
238
+ const collectionConfig = payload.collections[collectionSlug]?.config;
239
+ if (!collectionConfig)
240
+ return null;
241
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
242
+ const snakeSlugField = slugField2;
243
+ const localesTableName = `${tableName}_locales`;
244
+ const drizzleDb = payload.db.drizzle;
245
+ if (!drizzleDb?.execute) {
246
+ const doc = await payload.findByID({
247
+ collection: collectionSlug,
248
+ id,
249
+ locale: "all",
250
+ select: { [slugField2]: true }
251
+ });
252
+ return doc?.[slugField2] ?? null;
253
+ }
254
+ const { sql: drizzleSql } = await import("drizzle-orm");
255
+ const slugFieldConfig = collectionConfig.fields?.find((f) => f && f.name === slugField2);
256
+ const slugIsLocalized = Boolean(slugFieldConfig?.localized);
257
+ if (!slugIsLocalized) {
258
+ const result2 = await drizzleDb.execute(drizzleSql`SELECT ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(tableName)} WHERE "id" = ${id}`);
259
+ const rows2 = result2.rows ?? [];
260
+ const slugValue = rows2[0]?.[snakeSlugField];
261
+ if (typeof slugValue !== "string")
262
+ return null;
263
+ const localization = payload.config.localization;
264
+ const localeCodes = localization ? localization.localeCodes ?? localization.locales.map((l) => l.code) : [];
265
+ if (localeCodes.length === 0)
266
+ return null;
267
+ return Object.fromEntries(localeCodes.map((code) => [code, slugValue]));
268
+ }
269
+ const result = await drizzleDb.execute(drizzleSql`SELECT _locale, ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(localesTableName)} WHERE "_parent_id" = ${id}`);
270
+ const rows = result.rows ?? [];
271
+ const out = {};
272
+ for (const r of rows) {
273
+ if (typeof r._locale === "string" && typeof r[snakeSlugField] === "string") {
274
+ out[r._locale] = r[snakeSlugField];
275
+ }
276
+ }
277
+ return out;
206
278
  }
207
279
 
208
280
  // src/exports/collections.ts
package/dist/metadata.js CHANGED
@@ -199,16 +199,55 @@ async function queryAllDocs({
199
199
  }) {
200
200
  return withUnstableCache([collectionSlug, slugField, locale], [createCollectionCacheKey({ collectionSlug, slug: "__all__", locale })], async () => {
201
201
  const payload = await getPayload({ config });
202
- const result = await payload.find({
203
- collection: collectionSlug,
204
- draft: false,
205
- limit: 1000,
206
- pagination: false,
207
- overrideAccess: false,
208
- select: { [slugField]: true },
209
- locale
210
- });
211
- return result.docs ?? [];
202
+ const collectionConfig = payload.collections[collectionSlug]?.config;
203
+ if (!collectionConfig)
204
+ return [];
205
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
206
+ const snakeSlugField = slugField;
207
+ const drizzleDb = payload.db.drizzle;
208
+ if (!drizzleDb?.execute) {
209
+ const result2 = await payload.find({
210
+ collection: collectionSlug,
211
+ draft: false,
212
+ limit: 1000,
213
+ pagination: false,
214
+ overrideAccess: true
215
+ });
216
+ return (result2.docs ?? []).map((d) => ({ [slugField]: d[slugField] }));
217
+ }
218
+ const hasVersions = Boolean(collectionConfig.versions);
219
+ const isSlugLocalized = (() => {
220
+ const fields = collectionConfig.fields;
221
+ if (!Array.isArray(fields))
222
+ return false;
223
+ const found = fields.find((f) => f.name === slugField);
224
+ return Boolean(found?.localized);
225
+ })();
226
+ const hasLocales = Boolean(collectionConfig.custom?._isLocalized) || Boolean(payload.config.localization?.locales?.length);
227
+ const localesTableName = `${tableName}_locales`;
228
+ let sqlText;
229
+ const safeLocale = locale.replace(/'/g, "''");
230
+ if (isSlugLocalized && hasLocales) {
231
+ const statusFilter = hasVersions ? ` AND "${tableName}"."_status" = 'published'` : "";
232
+ sqlText = `
233
+ SELECT DISTINCT ON ("${tableName}"."id")
234
+ "${tableName}"."id" AS "id",
235
+ "${localesTableName}"."${snakeSlugField}" AS "${snakeSlugField}",
236
+ "${tableName}"."updated_at" AS "updated_at"
237
+ FROM "${tableName}"
238
+ JOIN "${localesTableName}" ON "${localesTableName}"."_parent_id" = "${tableName}"."id"
239
+ WHERE "${localesTableName}"."_locale" = '${safeLocale}'${statusFilter}
240
+ ORDER BY "${tableName}"."id", "${tableName}"."created_at" DESC
241
+ LIMIT 1000
242
+ `;
243
+ } else {
244
+ const statusFilter = hasVersions ? ` WHERE "_status" = 'published'` : "";
245
+ sqlText = `SELECT id, "${snakeSlugField}", "updated_at" FROM "${tableName}"${statusFilter} ORDER BY "created_at" DESC LIMIT 1000`;
246
+ }
247
+ const { sql: drizzleSql } = await import("drizzle-orm");
248
+ const result = await drizzleDb.execute(drizzleSql.raw(sqlText));
249
+ const rows = result.rows ?? [];
250
+ return rows.map((r) => ({ [slugField]: r[snakeSlugField], id: r.id, updatedAt: r.updated_at }));
212
251
  });
213
252
  }
214
253
  async function queryAllLocaleSlugs({
@@ -218,13 +257,46 @@ async function queryAllLocaleSlugs({
218
257
  config
219
258
  }) {
220
259
  const payload = await getPayload({ config });
221
- const doc = await payload.findByID({
222
- collection: collectionSlug,
223
- id,
224
- locale: "all",
225
- select: { [slugField]: true }
226
- });
227
- return doc?.[slugField];
260
+ const collectionConfig = payload.collections[collectionSlug]?.config;
261
+ if (!collectionConfig)
262
+ return null;
263
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
264
+ const snakeSlugField = slugField;
265
+ const localesTableName = `${tableName}_locales`;
266
+ const drizzleDb = payload.db.drizzle;
267
+ if (!drizzleDb?.execute) {
268
+ const doc = await payload.findByID({
269
+ collection: collectionSlug,
270
+ id,
271
+ locale: "all",
272
+ select: { [slugField]: true }
273
+ });
274
+ return doc?.[slugField] ?? null;
275
+ }
276
+ const { sql: drizzleSql } = await import("drizzle-orm");
277
+ const slugFieldConfig = collectionConfig.fields?.find((f) => f && f.name === slugField);
278
+ const slugIsLocalized = Boolean(slugFieldConfig?.localized);
279
+ if (!slugIsLocalized) {
280
+ const result2 = await drizzleDb.execute(drizzleSql`SELECT ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(tableName)} WHERE "id" = ${id}`);
281
+ const rows2 = result2.rows ?? [];
282
+ const slugValue = rows2[0]?.[snakeSlugField];
283
+ if (typeof slugValue !== "string")
284
+ return null;
285
+ const localization = payload.config.localization;
286
+ const localeCodes = localization ? localization.localeCodes ?? localization.locales.map((l) => l.code) : [];
287
+ if (localeCodes.length === 0)
288
+ return null;
289
+ return Object.fromEntries(localeCodes.map((code) => [code, slugValue]));
290
+ }
291
+ const result = await drizzleDb.execute(drizzleSql`SELECT _locale, ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(localesTableName)} WHERE "_parent_id" = ${id}`);
292
+ const rows = result.rows ?? [];
293
+ const out = {};
294
+ for (const r of rows) {
295
+ if (typeof r._locale === "string" && typeof r[snakeSlugField] === "string") {
296
+ out[r._locale] = r[snakeSlugField];
297
+ }
298
+ }
299
+ return out;
228
300
  }
229
301
 
230
302
  // src/exports/metadata.ts
package/dist/pages.d.ts CHANGED
@@ -15,13 +15,23 @@ import { ImportMap as ImportMap3, SanitizedConfig as SanitizedConfig2 } from "pa
15
15
  import { ReactNode as ReactNode2 } from "react";
16
16
  type SlugShape = "single" | "catch-all";
17
17
  type NextPageProps = {
18
- params: Promise<{
18
+ params: {
19
19
  locale: string;
20
20
  slug?: string | string[];
21
- }>;
21
+ };
22
+ };
23
+ type RoutingConfig = {
24
+ locales: readonly string[];
25
+ defaultLocale: string;
26
+ localePrefix?: "always" | "as-needed" | "never" | {
27
+ mode: "always" | "as-needed" | "never";
28
+ prefixes?: Partial<Record<string, string>>;
29
+ };
30
+ localeDetection?: boolean;
31
+ domains?: unknown;
32
+ localeCookie?: unknown;
33
+ alternateLinks?: boolean;
22
34
  };
23
- import { RoutingConfig as GenericRoutingConfig } from "next-intl/routing";
24
- type RoutingConfig = GenericRoutingConfig<string[], any, any, any>;
25
35
  type CreateCollectionPageExportsArgs<S extends string = "pages"> = {
26
36
  slug?: S;
27
37
  config: Promise<SanitizedConfig2>;
@@ -32,6 +42,8 @@ type CreateCollectionPageExportsArgs<S extends string = "pages"> = {
32
42
  type CreateCollectionPageExportsDeps<S extends string> = {
33
43
  getServerSideURL: () => string;
34
44
  pagePathPrefix?: string;
45
+ generateMeta?: (...args: any[]) => any;
46
+ metadataType?: "website" | "article";
35
47
  };
36
48
  declare function createCollectionPageExports<S extends string = "pages">({ slug: collectionSlug, config: configPromise, importMap, routing, slugShape }: CreateCollectionPageExportsArgs<S>, { getServerSideURL, pagePathPrefix }: CreateCollectionPageExportsDeps<S>): {
37
49
  default: (props: NextPageProps) => Promise<ReactNode2>;
package/dist/pages.js CHANGED
@@ -146,7 +146,9 @@ var RenderBlocks = async ({ blocks, blockProps, config, importMap, locale, searc
146
146
  const block = blocks[i];
147
147
  const { blockType } = block;
148
148
  const blockConfig = (config.blocks ?? []).find((b) => b.slug === blockType);
149
- const customPath = blockConfig?.custom?.[name]?.path;
149
+ const blockCustom = blockConfig?.custom?.[name];
150
+ const customPath = blockCustom?.path;
151
+ const wantsSearchParams = blockCustom?.searchParams === true;
150
152
  const importMapPath = (typeof customPath === "string" ? customPath : null) ?? `${DEFAULT_BLOCK_PATH_PREFIX}/${blockType}`;
151
153
  const Block = await getFromImportMap(importMapPath, importMap);
152
154
  if (!Block) {
@@ -158,7 +160,7 @@ var RenderBlocks = async ({ blocks, blockProps, config, importMap, locale, searc
158
160
  ...blockProps,
159
161
  ...block,
160
162
  locale,
161
- searchParams
163
+ ...wantsSearchParams ? { searchParams } : null
162
164
  }, i));
163
165
  }
164
166
  return /* @__PURE__ */ jsx3(Fragment, {
@@ -286,16 +288,55 @@ async function queryAllDocs({
286
288
  }) {
287
289
  return withUnstableCache([collectionSlug, slugField, locale], [createCollectionCacheKey({ collectionSlug, slug: "__all__", locale })], async () => {
288
290
  const payload = await getPayload({ config });
289
- const result = await payload.find({
290
- collection: collectionSlug,
291
- draft: false,
292
- limit: 1000,
293
- pagination: false,
294
- overrideAccess: false,
295
- select: { [slugField]: true },
296
- locale
297
- });
298
- return result.docs ?? [];
291
+ const collectionConfig = payload.collections[collectionSlug]?.config;
292
+ if (!collectionConfig)
293
+ return [];
294
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
295
+ const snakeSlugField = slugField;
296
+ const drizzleDb = payload.db.drizzle;
297
+ if (!drizzleDb?.execute) {
298
+ const result2 = await payload.find({
299
+ collection: collectionSlug,
300
+ draft: false,
301
+ limit: 1000,
302
+ pagination: false,
303
+ overrideAccess: true
304
+ });
305
+ return (result2.docs ?? []).map((d) => ({ [slugField]: d[slugField] }));
306
+ }
307
+ const hasVersions = Boolean(collectionConfig.versions);
308
+ const isSlugLocalized = (() => {
309
+ const fields = collectionConfig.fields;
310
+ if (!Array.isArray(fields))
311
+ return false;
312
+ const found = fields.find((f) => f.name === slugField);
313
+ return Boolean(found?.localized);
314
+ })();
315
+ const hasLocales = Boolean(collectionConfig.custom?._isLocalized) || Boolean(payload.config.localization?.locales?.length);
316
+ const localesTableName = `${tableName}_locales`;
317
+ let sqlText;
318
+ const safeLocale = locale.replace(/'/g, "''");
319
+ if (isSlugLocalized && hasLocales) {
320
+ const statusFilter = hasVersions ? ` AND "${tableName}"."_status" = 'published'` : "";
321
+ sqlText = `
322
+ SELECT DISTINCT ON ("${tableName}"."id")
323
+ "${tableName}"."id" AS "id",
324
+ "${localesTableName}"."${snakeSlugField}" AS "${snakeSlugField}",
325
+ "${tableName}"."updated_at" AS "updated_at"
326
+ FROM "${tableName}"
327
+ JOIN "${localesTableName}" ON "${localesTableName}"."_parent_id" = "${tableName}"."id"
328
+ WHERE "${localesTableName}"."_locale" = '${safeLocale}'${statusFilter}
329
+ ORDER BY "${tableName}"."id", "${tableName}"."created_at" DESC
330
+ LIMIT 1000
331
+ `;
332
+ } else {
333
+ const statusFilter = hasVersions ? ` WHERE "_status" = 'published'` : "";
334
+ sqlText = `SELECT id, "${snakeSlugField}", "updated_at" FROM "${tableName}"${statusFilter} ORDER BY "created_at" DESC LIMIT 1000`;
335
+ }
336
+ const { sql: drizzleSql } = await import("drizzle-orm");
337
+ const result = await drizzleDb.execute(drizzleSql.raw(sqlText));
338
+ const rows = result.rows ?? [];
339
+ return rows.map((r) => ({ [slugField]: r[snakeSlugField], id: r.id, updatedAt: r.updated_at }));
299
340
  });
300
341
  }
301
342
  async function queryAllLocaleSlugs({
@@ -305,13 +346,46 @@ async function queryAllLocaleSlugs({
305
346
  config
306
347
  }) {
307
348
  const payload = await getPayload({ config });
308
- const doc = await payload.findByID({
309
- collection: collectionSlug,
310
- id,
311
- locale: "all",
312
- select: { [slugField]: true }
313
- });
314
- return doc?.[slugField];
349
+ const collectionConfig = payload.collections[collectionSlug]?.config;
350
+ if (!collectionConfig)
351
+ return null;
352
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
353
+ const snakeSlugField = slugField;
354
+ const localesTableName = `${tableName}_locales`;
355
+ const drizzleDb = payload.db.drizzle;
356
+ if (!drizzleDb?.execute) {
357
+ const doc = await payload.findByID({
358
+ collection: collectionSlug,
359
+ id,
360
+ locale: "all",
361
+ select: { [slugField]: true }
362
+ });
363
+ return doc?.[slugField] ?? null;
364
+ }
365
+ const { sql: drizzleSql } = await import("drizzle-orm");
366
+ const slugFieldConfig = collectionConfig.fields?.find((f) => f && f.name === slugField);
367
+ const slugIsLocalized = Boolean(slugFieldConfig?.localized);
368
+ if (!slugIsLocalized) {
369
+ const result2 = await drizzleDb.execute(drizzleSql`SELECT ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(tableName)} WHERE "id" = ${id}`);
370
+ const rows2 = result2.rows ?? [];
371
+ const slugValue = rows2[0]?.[snakeSlugField];
372
+ if (typeof slugValue !== "string")
373
+ return null;
374
+ const localization = payload.config.localization;
375
+ const localeCodes = localization ? localization.localeCodes ?? localization.locales.map((l) => l.code) : [];
376
+ if (localeCodes.length === 0)
377
+ return null;
378
+ return Object.fromEntries(localeCodes.map((code) => [code, slugValue]));
379
+ }
380
+ const result = await drizzleDb.execute(drizzleSql`SELECT _locale, ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(localesTableName)} WHERE "_parent_id" = ${id}`);
381
+ const rows = result.rows ?? [];
382
+ const out = {};
383
+ for (const r of rows) {
384
+ if (typeof r._locale === "string" && typeof r[snakeSlugField] === "string") {
385
+ out[r._locale] = r[snakeSlugField];
386
+ }
387
+ }
388
+ return out;
315
389
  }
316
390
 
317
391
  // src/render/pages/createCollectionPageExports.tsx
@@ -390,6 +464,10 @@ function createCollectionPageExports({
390
464
  });
391
465
  }
392
466
  const default_ = async (props) => {
467
+ if (!props || !props.params) {
468
+ const { notFound } = await import("next/navigation");
469
+ notFound();
470
+ }
393
471
  const params = await props.params;
394
472
  const locale = params.locale;
395
473
  if (!routing.locales.includes(locale)) {
@@ -18,10 +18,10 @@ import { ReactNode as ReactNode2 } from "react";
18
18
  type SlugShape = "single" | "catch-all";
19
19
  import { ReactNode } from "react";
20
20
  type NextPageProps = {
21
- params: Promise<{
21
+ params: {
22
22
  locale: string;
23
23
  slug?: string | string[];
24
- }>;
24
+ };
25
25
  };
26
26
  type NextLayoutProps = {
27
27
  params: Promise<{
@@ -29,8 +29,18 @@ type NextLayoutProps = {
29
29
  }>;
30
30
  children: ReactNode;
31
31
  };
32
- import { RoutingConfig as GenericRoutingConfig } from "next-intl/routing";
33
- type RoutingConfig = GenericRoutingConfig<string[], any, any, any>;
32
+ type RoutingConfig = {
33
+ locales: readonly string[];
34
+ defaultLocale: string;
35
+ localePrefix?: "always" | "as-needed" | "never" | {
36
+ mode: "always" | "as-needed" | "never";
37
+ prefixes?: Partial<Record<string, string>>;
38
+ };
39
+ localeDetection?: boolean;
40
+ domains?: unknown;
41
+ localeCookie?: unknown;
42
+ alternateLinks?: boolean;
43
+ };
34
44
  type CreateCollectionPageExportsArgs<S extends string = "pages"> = {
35
45
  slug?: S;
36
46
  config: Promise<SanitizedConfig2>;
@@ -41,6 +51,8 @@ type CreateCollectionPageExportsArgs<S extends string = "pages"> = {
41
51
  type CreateCollectionPageExportsDeps<S extends string> = {
42
52
  getServerSideURL: () => string;
43
53
  pagePathPrefix?: string;
54
+ generateMeta?: (...args: any[]) => any;
55
+ metadataType?: "website" | "article";
44
56
  };
45
57
  declare function createCollectionPageExports<S extends string = "pages">({ slug: collectionSlug, config: configPromise, importMap, routing, slugShape }: CreateCollectionPageExportsArgs<S>, { getServerSideURL, pagePathPrefix }: CreateCollectionPageExportsDeps<S>): {
46
58
  default: (props: NextPageProps) => Promise<ReactNode2>;
@@ -146,7 +146,9 @@ var RenderBlocks = async ({ blocks, blockProps, config, importMap, locale, searc
146
146
  const block = blocks[i];
147
147
  const { blockType } = block;
148
148
  const blockConfig = (config.blocks ?? []).find((b) => b.slug === blockType);
149
- const customPath = blockConfig?.custom?.[name]?.path;
149
+ const blockCustom = blockConfig?.custom?.[name];
150
+ const customPath = blockCustom?.path;
151
+ const wantsSearchParams = blockCustom?.searchParams === true;
150
152
  const importMapPath = (typeof customPath === "string" ? customPath : null) ?? `${DEFAULT_BLOCK_PATH_PREFIX}/${blockType}`;
151
153
  const Block = await getFromImportMap(importMapPath, importMap);
152
154
  if (!Block) {
@@ -158,7 +160,7 @@ var RenderBlocks = async ({ blocks, blockProps, config, importMap, locale, searc
158
160
  ...blockProps,
159
161
  ...block,
160
162
  locale,
161
- searchParams
163
+ ...wantsSearchParams ? { searchParams } : null
162
164
  }, i));
163
165
  }
164
166
  return /* @__PURE__ */ jsx3(Fragment, {
@@ -316,16 +318,55 @@ async function queryAllDocs({
316
318
  }) {
317
319
  return withUnstableCache([collectionSlug, slugField, locale], [createCollectionCacheKey({ collectionSlug, slug: "__all__", locale })], async () => {
318
320
  const payload = await getPayload({ config });
319
- const result = await payload.find({
320
- collection: collectionSlug,
321
- draft: false,
322
- limit: 1000,
323
- pagination: false,
324
- overrideAccess: false,
325
- select: { [slugField]: true },
326
- locale
327
- });
328
- return result.docs ?? [];
321
+ const collectionConfig = payload.collections[collectionSlug]?.config;
322
+ if (!collectionConfig)
323
+ return [];
324
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
325
+ const snakeSlugField = slugField;
326
+ const drizzleDb = payload.db.drizzle;
327
+ if (!drizzleDb?.execute) {
328
+ const result2 = await payload.find({
329
+ collection: collectionSlug,
330
+ draft: false,
331
+ limit: 1000,
332
+ pagination: false,
333
+ overrideAccess: true
334
+ });
335
+ return (result2.docs ?? []).map((d) => ({ [slugField]: d[slugField] }));
336
+ }
337
+ const hasVersions = Boolean(collectionConfig.versions);
338
+ const isSlugLocalized = (() => {
339
+ const fields = collectionConfig.fields;
340
+ if (!Array.isArray(fields))
341
+ return false;
342
+ const found = fields.find((f) => f.name === slugField);
343
+ return Boolean(found?.localized);
344
+ })();
345
+ const hasLocales = Boolean(collectionConfig.custom?._isLocalized) || Boolean(payload.config.localization?.locales?.length);
346
+ const localesTableName = `${tableName}_locales`;
347
+ let sqlText;
348
+ const safeLocale = locale.replace(/'/g, "''");
349
+ if (isSlugLocalized && hasLocales) {
350
+ const statusFilter = hasVersions ? ` AND "${tableName}"."_status" = 'published'` : "";
351
+ sqlText = `
352
+ SELECT DISTINCT ON ("${tableName}"."id")
353
+ "${tableName}"."id" AS "id",
354
+ "${localesTableName}"."${snakeSlugField}" AS "${snakeSlugField}",
355
+ "${tableName}"."updated_at" AS "updated_at"
356
+ FROM "${tableName}"
357
+ JOIN "${localesTableName}" ON "${localesTableName}"."_parent_id" = "${tableName}"."id"
358
+ WHERE "${localesTableName}"."_locale" = '${safeLocale}'${statusFilter}
359
+ ORDER BY "${tableName}"."id", "${tableName}"."created_at" DESC
360
+ LIMIT 1000
361
+ `;
362
+ } else {
363
+ const statusFilter = hasVersions ? ` WHERE "_status" = 'published'` : "";
364
+ sqlText = `SELECT id, "${snakeSlugField}", "updated_at" FROM "${tableName}"${statusFilter} ORDER BY "created_at" DESC LIMIT 1000`;
365
+ }
366
+ const { sql: drizzleSql } = await import("drizzle-orm");
367
+ const result = await drizzleDb.execute(drizzleSql.raw(sqlText));
368
+ const rows = result.rows ?? [];
369
+ return rows.map((r) => ({ [slugField]: r[snakeSlugField], id: r.id, updatedAt: r.updated_at }));
329
370
  });
330
371
  }
331
372
  async function queryAllLocaleSlugs({
@@ -335,13 +376,46 @@ async function queryAllLocaleSlugs({
335
376
  config
336
377
  }) {
337
378
  const payload = await getPayload({ config });
338
- const doc = await payload.findByID({
339
- collection: collectionSlug,
340
- id,
341
- locale: "all",
342
- select: { [slugField]: true }
343
- });
344
- return doc?.[slugField];
379
+ const collectionConfig = payload.collections[collectionSlug]?.config;
380
+ if (!collectionConfig)
381
+ return null;
382
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
383
+ const snakeSlugField = slugField;
384
+ const localesTableName = `${tableName}_locales`;
385
+ const drizzleDb = payload.db.drizzle;
386
+ if (!drizzleDb?.execute) {
387
+ const doc = await payload.findByID({
388
+ collection: collectionSlug,
389
+ id,
390
+ locale: "all",
391
+ select: { [slugField]: true }
392
+ });
393
+ return doc?.[slugField] ?? null;
394
+ }
395
+ const { sql: drizzleSql } = await import("drizzle-orm");
396
+ const slugFieldConfig = collectionConfig.fields?.find((f) => f && f.name === slugField);
397
+ const slugIsLocalized = Boolean(slugFieldConfig?.localized);
398
+ if (!slugIsLocalized) {
399
+ const result2 = await drizzleDb.execute(drizzleSql`SELECT ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(tableName)} WHERE "id" = ${id}`);
400
+ const rows2 = result2.rows ?? [];
401
+ const slugValue = rows2[0]?.[snakeSlugField];
402
+ if (typeof slugValue !== "string")
403
+ return null;
404
+ const localization = payload.config.localization;
405
+ const localeCodes = localization ? localization.localeCodes ?? localization.locales.map((l) => l.code) : [];
406
+ if (localeCodes.length === 0)
407
+ return null;
408
+ return Object.fromEntries(localeCodes.map((code) => [code, slugValue]));
409
+ }
410
+ const result = await drizzleDb.execute(drizzleSql`SELECT _locale, ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(localesTableName)} WHERE "_parent_id" = ${id}`);
411
+ const rows = result.rows ?? [];
412
+ const out = {};
413
+ for (const r of rows) {
414
+ if (typeof r._locale === "string" && typeof r[snakeSlugField] === "string") {
415
+ out[r._locale] = r[snakeSlugField];
416
+ }
417
+ }
418
+ return out;
345
419
  }
346
420
 
347
421
  // src/render/pages/createCollectionPageExports.tsx
@@ -420,6 +494,10 @@ function createCollectionPageExports({
420
494
  });
421
495
  }
422
496
  const default_ = async (props) => {
497
+ if (!props || !props.params) {
498
+ const { notFound } = await import("next/navigation");
499
+ notFound();
500
+ }
423
501
  const params = await props.params;
424
502
  const locale = params.locale;
425
503
  if (!routing.locales.includes(locale)) {
package/dist/sitemap.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  type CreateCollectionPageExportsDeps<S extends string> = {
2
2
  getServerSideURL: () => string;
3
3
  pagePathPrefix?: string;
4
+ generateMeta?: (...args: any[]) => any;
5
+ metadataType?: "website" | "article";
4
6
  };
5
7
  type GenerateSitemapPageExportsArgs = CreateCollectionPageExportsDeps<any>;
6
8
  declare function createSitemapFromCollections(...args: GenerateSitemapPageExportsArgs[]): () => Response;
package/dist/sitemap.js CHANGED
@@ -109,16 +109,55 @@ async function queryAllDocs({
109
109
  }) {
110
110
  return withUnstableCache([collectionSlug, slugField, locale], [createCollectionCacheKey({ collectionSlug, slug: "__all__", locale })], async () => {
111
111
  const payload = await getPayload({ config });
112
- const result = await payload.find({
113
- collection: collectionSlug,
114
- draft: false,
115
- limit: 1000,
116
- pagination: false,
117
- overrideAccess: false,
118
- select: { [slugField]: true },
119
- locale
120
- });
121
- return result.docs ?? [];
112
+ const collectionConfig = payload.collections[collectionSlug]?.config;
113
+ if (!collectionConfig)
114
+ return [];
115
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
116
+ const snakeSlugField = slugField;
117
+ const drizzleDb = payload.db.drizzle;
118
+ if (!drizzleDb?.execute) {
119
+ const result2 = await payload.find({
120
+ collection: collectionSlug,
121
+ draft: false,
122
+ limit: 1000,
123
+ pagination: false,
124
+ overrideAccess: true
125
+ });
126
+ return (result2.docs ?? []).map((d) => ({ [slugField]: d[slugField] }));
127
+ }
128
+ const hasVersions = Boolean(collectionConfig.versions);
129
+ const isSlugLocalized = (() => {
130
+ const fields = collectionConfig.fields;
131
+ if (!Array.isArray(fields))
132
+ return false;
133
+ const found = fields.find((f) => f.name === slugField);
134
+ return Boolean(found?.localized);
135
+ })();
136
+ const hasLocales = Boolean(collectionConfig.custom?._isLocalized) || Boolean(payload.config.localization?.locales?.length);
137
+ const localesTableName = `${tableName}_locales`;
138
+ let sqlText;
139
+ const safeLocale = locale.replace(/'/g, "''");
140
+ if (isSlugLocalized && hasLocales) {
141
+ const statusFilter = hasVersions ? ` AND "${tableName}"."_status" = 'published'` : "";
142
+ sqlText = `
143
+ SELECT DISTINCT ON ("${tableName}"."id")
144
+ "${tableName}"."id" AS "id",
145
+ "${localesTableName}"."${snakeSlugField}" AS "${snakeSlugField}",
146
+ "${tableName}"."updated_at" AS "updated_at"
147
+ FROM "${tableName}"
148
+ JOIN "${localesTableName}" ON "${localesTableName}"."_parent_id" = "${tableName}"."id"
149
+ WHERE "${localesTableName}"."_locale" = '${safeLocale}'${statusFilter}
150
+ ORDER BY "${tableName}"."id", "${tableName}"."created_at" DESC
151
+ LIMIT 1000
152
+ `;
153
+ } else {
154
+ const statusFilter = hasVersions ? ` WHERE "_status" = 'published'` : "";
155
+ sqlText = `SELECT id, "${snakeSlugField}", "updated_at" FROM "${tableName}"${statusFilter} ORDER BY "created_at" DESC LIMIT 1000`;
156
+ }
157
+ const { sql: drizzleSql } = await import("drizzle-orm");
158
+ const result = await drizzleDb.execute(drizzleSql.raw(sqlText));
159
+ const rows = result.rows ?? [];
160
+ return rows.map((r) => ({ [slugField]: r[snakeSlugField], id: r.id, updatedAt: r.updated_at }));
122
161
  });
123
162
  }
124
163
  async function queryAllLocaleSlugs({
@@ -128,13 +167,46 @@ async function queryAllLocaleSlugs({
128
167
  config
129
168
  }) {
130
169
  const payload = await getPayload({ config });
131
- const doc = await payload.findByID({
132
- collection: collectionSlug,
133
- id,
134
- locale: "all",
135
- select: { [slugField]: true }
136
- });
137
- return doc?.[slugField];
170
+ const collectionConfig = payload.collections[collectionSlug]?.config;
171
+ if (!collectionConfig)
172
+ return null;
173
+ const tableName = collectionConfig.dbName ?? collectionConfig.slug;
174
+ const snakeSlugField = slugField;
175
+ const localesTableName = `${tableName}_locales`;
176
+ const drizzleDb = payload.db.drizzle;
177
+ if (!drizzleDb?.execute) {
178
+ const doc = await payload.findByID({
179
+ collection: collectionSlug,
180
+ id,
181
+ locale: "all",
182
+ select: { [slugField]: true }
183
+ });
184
+ return doc?.[slugField] ?? null;
185
+ }
186
+ const { sql: drizzleSql } = await import("drizzle-orm");
187
+ const slugFieldConfig = collectionConfig.fields?.find((f) => f && f.name === slugField);
188
+ const slugIsLocalized = Boolean(slugFieldConfig?.localized);
189
+ if (!slugIsLocalized) {
190
+ const result2 = await drizzleDb.execute(drizzleSql`SELECT ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(tableName)} WHERE "id" = ${id}`);
191
+ const rows2 = result2.rows ?? [];
192
+ const slugValue = rows2[0]?.[snakeSlugField];
193
+ if (typeof slugValue !== "string")
194
+ return null;
195
+ const localization = payload.config.localization;
196
+ const localeCodes = localization ? localization.localeCodes ?? localization.locales.map((l) => l.code) : [];
197
+ if (localeCodes.length === 0)
198
+ return null;
199
+ return Object.fromEntries(localeCodes.map((code) => [code, slugValue]));
200
+ }
201
+ const result = await drizzleDb.execute(drizzleSql`SELECT _locale, ${drizzleSql.identifier(snakeSlugField)} FROM ${drizzleSql.identifier(localesTableName)} WHERE "_parent_id" = ${id}`);
202
+ const rows = result.rows ?? [];
203
+ const out = {};
204
+ for (const r of rows) {
205
+ if (typeof r._locale === "string" && typeof r[snakeSlugField] === "string") {
206
+ out[r._locale] = r[snakeSlugField];
207
+ }
208
+ }
209
+ return out;
138
210
  }
139
211
 
140
212
  // src/render/pages/createCollectionPageExports.tsx
@@ -234,6 +306,10 @@ function createCollectionPageExports({
234
306
  });
235
307
  }
236
308
  const default_ = async (props) => {
309
+ if (!props || !props.params) {
310
+ const { notFound } = await import("next/navigation");
311
+ notFound();
312
+ }
237
313
  const params = await props.params;
238
314
  const locale = params.locale;
239
315
  if (!routing.locales.includes(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": "1.2.1",
4
+ "version": "1.3.0",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "files": [
@@ -109,6 +109,7 @@
109
109
  "dependencies": {
110
110
  "@payloadcms/live-preview-react": "3.85.0",
111
111
  "@payloadcms/plugin-mcp": "3.85.0",
112
+ "drizzle-orm": "0.45.2",
112
113
  "@payloadcms/richtext-lexical": "3.85.0",
113
114
  "@payloadcms/next": "3.85.0",
114
115
  "next": "16.2.6",