@justanarthur/payload-www 0.3.5 → 1.2.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 +317 -307
- package/dist/access.d.ts +2 -2
- package/dist/access.js +3 -17
- package/dist/blocks.d.ts +7 -12
- package/dist/blocks.js +24 -43
- package/dist/cli.js +201 -0
- package/dist/collections.d.ts +33 -225
- package/dist/collections.js +185 -639
- package/dist/config.d.ts +19 -105
- package/dist/config.js +257 -855
- package/dist/fields.d.ts +4 -46
- package/dist/fields.js +177 -53
- package/dist/imagehash.js +0 -15
- package/dist/import-map-provider.d.ts +0 -16
- package/dist/import-map-provider.js +0 -15
- package/dist/metadata.d.ts +24 -89
- package/dist/metadata.js +123 -255
- package/dist/pages.d.ts +37 -319
- package/dist/pages.js +349 -964
- package/dist/render-pages.d.ts +70 -565
- package/dist/render-pages.js +464 -1477
- package/dist/sitemap.d.ts +10 -0
- package/dist/sitemap.js +337 -0
- package/dist/translator.js +0 -15
- package/dist/utils.d.ts +10 -12
- package/dist/utils.js +18 -41
- package/package.json +81 -178
- package/dist/components.d.ts +0 -6
- package/dist/components.js +0 -39
- package/dist/core-access.d.ts +0 -11
- package/dist/core-access.js +0 -34
- package/dist/core-blocks.d.ts +0 -24
- package/dist/core-blocks.js +0 -79
- package/dist/core-fields.d.ts +0 -73
- package/dist/core-fields.js +0 -167
- package/dist/core-utils.d.ts +0 -16
- package/dist/core-utils.js +0 -63
- package/dist/data-collections.d.ts +0 -229
- package/dist/data-collections.js +0 -676
- package/dist/data-seed.d.ts +0 -76
- package/dist/data-seed.js +0 -212
- package/dist/data-test.d.ts +0 -30
- package/dist/data-test.js +0 -1110
- package/dist/globals.d.ts +0 -69
- package/dist/globals.js +0 -251
- package/dist/hooks.d.ts +0 -108
- package/dist/hooks.js +0 -228
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -39
- package/dist/render-components.d.ts +0 -42
- package/dist/render-components.js +0 -154
- package/dist/render-metadata.d.ts +0 -122
- package/dist/render-metadata.js +0 -390
- package/dist/render-utils.d.ts +0 -166
- package/dist/render-utils.js +0 -398
- package/dist/seed.d.ts +0 -76
- package/dist/seed.js +0 -212
- package/dist/server.d.ts +0 -980
- package/dist/server.js +0 -2231
- package/dist/test.d.ts +0 -30
- package/dist/test.js +0 -1110
- package/dist/with-www-config.d.ts +0 -108
- package/dist/with-www-config.js +0 -1006
package/dist/data-test.js
DELETED
|
@@ -1,1110 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
-
|
|
18
|
-
// src/data/test/createTestPayload.ts
|
|
19
|
-
import { sqliteAdapter } from "@payloadcms/db-sqlite";
|
|
20
|
-
import { flattenAllFields, getPayload as getPayload2, inMemoryKVAdapter } from "payload";
|
|
21
|
-
import path from "node:path";
|
|
22
|
-
import { fileURLToPath } from "node:url";
|
|
23
|
-
|
|
24
|
-
// src/core/access/index.ts
|
|
25
|
-
var anyone = () => true;
|
|
26
|
-
var authenticated = ({ req: { user } }) => Boolean(user);
|
|
27
|
-
var authenticatedOrPublished = ({ req: { user } }) => {
|
|
28
|
-
if (user)
|
|
29
|
-
return true;
|
|
30
|
-
return { _status: { equals: "published" } };
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// src/core/fields/slug.ts
|
|
34
|
-
var FLAT_PATTERN = /^[a-z0-9-]+$/;
|
|
35
|
-
var NESTED_PATTERN = /^[a-z0-9_-]+$/;
|
|
36
|
-
var slugField = (options = {}) => {
|
|
37
|
-
const { name = "slug", nested = false, localized = true } = options;
|
|
38
|
-
const pattern = nested ? NESTED_PATTERN : FLAT_PATTERN;
|
|
39
|
-
const invalidMessage = nested ? "Slug must be lowercase, with hyphens or the `_` nesting divider (no spaces or other special characters)" : "Slug must be lowercase, with hyphens (no spaces or special characters)";
|
|
40
|
-
return {
|
|
41
|
-
name,
|
|
42
|
-
type: "text",
|
|
43
|
-
required: true,
|
|
44
|
-
unique: true,
|
|
45
|
-
index: true,
|
|
46
|
-
localized,
|
|
47
|
-
admin: {
|
|
48
|
-
position: "sidebar",
|
|
49
|
-
...nested ? { description: "Lowercase, hyphens for words. Use the `_` divider for nesting, e.g. `about_us` → `/about/us`." } : {}
|
|
50
|
-
},
|
|
51
|
-
validate: (value) => {
|
|
52
|
-
if (typeof value !== "string")
|
|
53
|
-
return "Slug must be a string";
|
|
54
|
-
if (value !== "" && !pattern.test(value))
|
|
55
|
-
return invalidMessage;
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// src/render/_locale.ts
|
|
62
|
-
function prefixFor(locale, defaultLocale, mode) {
|
|
63
|
-
let result;
|
|
64
|
-
if (mode === "never")
|
|
65
|
-
result = "";
|
|
66
|
-
else if (mode === "as-needed" && locale === defaultLocale)
|
|
67
|
-
result = "";
|
|
68
|
-
else
|
|
69
|
-
result = `/${locale}`;
|
|
70
|
-
console.log("[WWW] render/_locale:prefixFor locale=", locale, "default=", defaultLocale, "mode=", mode, "->", JSON.stringify(result));
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
function resolveLocale(req) {
|
|
74
|
-
if (!req || typeof req !== "object") {
|
|
75
|
-
console.log('[WWW] render/_locale:resolveLocale -> "" (no req)');
|
|
76
|
-
return "";
|
|
77
|
-
}
|
|
78
|
-
const r = req;
|
|
79
|
-
if (typeof r.locale === "string" && r.locale.length > 0) {
|
|
80
|
-
console.log("[WWW] render/_locale:resolveLocale ->", r.locale, "(from req.locale)");
|
|
81
|
-
return r.locale;
|
|
82
|
-
}
|
|
83
|
-
const fallback = r.payload?.config?.localization?.defaultLocale;
|
|
84
|
-
if (typeof fallback === "string" && fallback.length > 0) {
|
|
85
|
-
console.log("[WWW] render/_locale:resolveLocale ->", fallback, "(from config.localization.defaultLocale)");
|
|
86
|
-
return fallback;
|
|
87
|
-
}
|
|
88
|
-
console.log('[WWW] render/_locale:resolveLocale -> "" (no locale anywhere)');
|
|
89
|
-
return "";
|
|
90
|
-
}
|
|
91
|
-
function allLocales(req) {
|
|
92
|
-
if (!req || typeof req !== "object") {
|
|
93
|
-
console.log("[WWW] render/_locale:allLocales -> [] (no req)");
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
|
-
const r = req;
|
|
97
|
-
const list = r.payload?.config?.localization?.locales;
|
|
98
|
-
if (!Array.isArray(list) || list.length === 0) {
|
|
99
|
-
console.log("[WWW] render/_locale:allLocales -> [] (no locales declared)");
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
const out = [];
|
|
103
|
-
for (const entry of list) {
|
|
104
|
-
if (typeof entry === "string" && entry.length > 0) {
|
|
105
|
-
out.push(entry);
|
|
106
|
-
} else if (entry && typeof entry === "object" && "code" in entry) {
|
|
107
|
-
const code = entry.code;
|
|
108
|
-
if (typeof code === "string" && code.length > 0)
|
|
109
|
-
out.push(code);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
console.log("[WWW] render/_locale:allLocales ->", JSON.stringify(out));
|
|
113
|
-
return out;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// src/render/hooks/_shared.ts
|
|
117
|
-
var cachePromise = null;
|
|
118
|
-
function nextCacheImport() {
|
|
119
|
-
return cachePromise ??= import("next/cache");
|
|
120
|
-
}
|
|
121
|
-
function shouldSkipRevalidate(context) {
|
|
122
|
-
const skip = Boolean(context?.disableRevalidate);
|
|
123
|
-
if (skip)
|
|
124
|
-
console.log("[WWW] render/hooks:_shared:shouldSkipRevalidate -> skip");
|
|
125
|
-
return skip;
|
|
126
|
-
}
|
|
127
|
-
async function safeRevalidatePath(payload, path) {
|
|
128
|
-
console.log("[WWW] render/hooks:_shared:safeRevalidatePath path=", path);
|
|
129
|
-
try {
|
|
130
|
-
const { revalidatePath } = await nextCacheImport();
|
|
131
|
-
revalidatePath(path);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
console.error("[WWW] render/hooks:_shared:safeRevalidatePath failed path=", path, "err=", String(error));
|
|
134
|
-
payload.logger.error(`revalidatePath("${path}") failed: ${String(error)}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async function safeRevalidateTag(payload, tag, profile = "max") {
|
|
138
|
-
console.log("[WWW] render/hooks:_shared:safeRevalidateTag tag=", tag, "profile=", profile);
|
|
139
|
-
try {
|
|
140
|
-
const { revalidateTag } = await nextCacheImport();
|
|
141
|
-
revalidateTag(tag, profile);
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error("[WWW] render/hooks:_shared:safeRevalidateTag failed tag=", tag, "err=", String(error));
|
|
144
|
-
payload.logger.error(`revalidateTag("${tag}") failed: ${String(error)}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// src/render/hooks/revalidateCollection.ts
|
|
149
|
-
function createRevalidateCollectionHook(options) {
|
|
150
|
-
const {
|
|
151
|
-
collectionSlug,
|
|
152
|
-
urlPathPrefix = "",
|
|
153
|
-
sitemapTag,
|
|
154
|
-
localePrefix: modeOption,
|
|
155
|
-
defaultLocale: defaultLocaleOption,
|
|
156
|
-
pathMode = "url"
|
|
157
|
-
} = options;
|
|
158
|
-
const resolvedSitemapTag = sitemapTag === false ? false : sitemapTag ?? `${collectionSlug}-sitemap`;
|
|
159
|
-
console.log("[WWW] render/hooks:createRevalidateCollectionHook collectionSlug=", collectionSlug, "urlPathPrefix=", urlPathPrefix, "sitemapTag=", resolvedSitemapTag, "pathMode=", pathMode);
|
|
160
|
-
const resolveDefaults = (req) => {
|
|
161
|
-
const mode = modeOption ?? "always";
|
|
162
|
-
const defaultLocale = defaultLocaleOption ?? req?.payload?.config?.localization?.defaultLocale ?? "";
|
|
163
|
-
return { mode, defaultLocale };
|
|
164
|
-
};
|
|
165
|
-
const localesToFanOut = (req) => {
|
|
166
|
-
const all = allLocales(req);
|
|
167
|
-
if (all.length > 0)
|
|
168
|
-
return all;
|
|
169
|
-
const single = resolveLocale(req);
|
|
170
|
-
return single ? [single] : [];
|
|
171
|
-
};
|
|
172
|
-
const collectionPath = (slug, locale, mode, defaultLocale) => {
|
|
173
|
-
const prefix = prefixFor(locale, defaultLocale, mode);
|
|
174
|
-
if (!slug)
|
|
175
|
-
return `${prefix}${urlPathPrefix}` || "/";
|
|
176
|
-
return `${prefix}${urlPathPrefix}/${slug}`;
|
|
177
|
-
};
|
|
178
|
-
const fanOutPaths = async (payload, req, slug, label) => {
|
|
179
|
-
const { mode, defaultLocale } = resolveDefaults(req);
|
|
180
|
-
const locales = localesToFanOut(req);
|
|
181
|
-
for (const locale of locales) {
|
|
182
|
-
const path = collectionPath(slug, locale, mode, defaultLocale);
|
|
183
|
-
payload.logger.info?.(`${label} ${path}`);
|
|
184
|
-
await safeRevalidatePath(payload, path);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
const fireCollectionTags = async (payload, docId, req) => {
|
|
188
|
-
console.log("[WWW] render/hooks:fireCollectionTags collectionSlug=", collectionSlug, "docId=", docId);
|
|
189
|
-
if (typeof docId === "string" || typeof docId === "number") {
|
|
190
|
-
await safeRevalidateTag(payload, `collection_${collectionSlug}_${docId}`);
|
|
191
|
-
}
|
|
192
|
-
if (resolvedSitemapTag !== false) {
|
|
193
|
-
await safeRevalidateTag(payload, resolvedSitemapTag);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
const afterChange = async ({ doc, previousDoc, req }) => {
|
|
197
|
-
if (shouldSkipRevalidate(req.context))
|
|
198
|
-
return doc;
|
|
199
|
-
const { payload } = req;
|
|
200
|
-
const typed = doc;
|
|
201
|
-
const prev = previousDoc;
|
|
202
|
-
console.log("[WWW] render/hooks:afterChange collectionSlug=", collectionSlug, "id=", typed.id, "slug=", typed.slug, "prevSlug=", prev?.slug);
|
|
203
|
-
const isPublished = typed._status === "published";
|
|
204
|
-
const wasPublished = prev?._status === "published";
|
|
205
|
-
const prevSlugIsString = typeof prev?.slug === "string";
|
|
206
|
-
const slugChanged = prevSlugIsString && prev?.slug !== typed.slug;
|
|
207
|
-
if (isPublished) {
|
|
208
|
-
if (pathMode !== "tag-only") {
|
|
209
|
-
await fanOutPaths(payload, req, typed.slug, `Revalidating ${collectionSlug} at path:`);
|
|
210
|
-
}
|
|
211
|
-
await fireCollectionTags(payload, typed.id, req);
|
|
212
|
-
}
|
|
213
|
-
if (wasPublished && (!isPublished || slugChanged)) {
|
|
214
|
-
if (pathMode !== "tag-only") {
|
|
215
|
-
await fanOutPaths(payload, req, prev?.slug, `Revalidating old ${collectionSlug} at path:`);
|
|
216
|
-
}
|
|
217
|
-
await fireCollectionTags(payload, typed.id, req);
|
|
218
|
-
}
|
|
219
|
-
return doc;
|
|
220
|
-
};
|
|
221
|
-
const afterDelete = async ({ doc, req }) => {
|
|
222
|
-
if (shouldSkipRevalidate(req.context))
|
|
223
|
-
return doc ?? null;
|
|
224
|
-
const { payload } = req;
|
|
225
|
-
const typed = doc;
|
|
226
|
-
console.log("[WWW] render/hooks:afterDelete collectionSlug=", collectionSlug, "id=", typed?.id, "slug=", typed?.slug);
|
|
227
|
-
if (pathMode !== "tag-only") {
|
|
228
|
-
await fanOutPaths(payload, req, typed?.slug, `Revalidating deleted ${collectionSlug} at path:`);
|
|
229
|
-
}
|
|
230
|
-
await fireCollectionTags(payload, typed?.id, req);
|
|
231
|
-
return doc ?? null;
|
|
232
|
-
};
|
|
233
|
-
return { afterChange, afterDelete };
|
|
234
|
-
}
|
|
235
|
-
var createRevalidatePageHooks = (opts = {}) => {
|
|
236
|
-
console.log("[WWW] render/hooks:createRevalidatePageHooks (deprecated alias) opts=", JSON.stringify(opts));
|
|
237
|
-
return createRevalidateCollectionHook({
|
|
238
|
-
collectionSlug: "pages",
|
|
239
|
-
urlPathPrefix: "",
|
|
240
|
-
...opts
|
|
241
|
-
});
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
// src/config/constants.ts
|
|
245
|
-
var PAGES_RENDER_PATH = "@justanarthur/payload-www/render-pages#PagesPage";
|
|
246
|
-
var POSTS_RENDER_PATH = "@justanarthur/payload-www/render-pages#PostsPage";
|
|
247
|
-
var HEADER_RENDER_PATH = "@justanarthur/payload-www/render-pages#HeaderPage";
|
|
248
|
-
var FOOTER_RENDER_PATH = "@justanarthur/payload-www/render-pages#FooterPage";
|
|
249
|
-
var LIVE_PREVIEW_LISTENER_PATH = "@justanarthur/payload-www/render-components#LivePreviewListener";
|
|
250
|
-
var PAGES_SLUG = "pages";
|
|
251
|
-
var POSTS_SLUG = "posts";
|
|
252
|
-
var STATIC_PAGES_SLUG = "staticPages";
|
|
253
|
-
|
|
254
|
-
// src/data/collections/Pages/index.ts
|
|
255
|
-
var HOME_PAGE_SLUG = "";
|
|
256
|
-
var PAGES_SLUG2 = PAGES_SLUG;
|
|
257
|
-
var createPagesCollection = (blocks, options = {}) => {
|
|
258
|
-
const {
|
|
259
|
-
renderPath = PAGES_RENDER_PATH,
|
|
260
|
-
slug: collectionSlug = PAGES_SLUG2,
|
|
261
|
-
localePrefix,
|
|
262
|
-
defaultLocale,
|
|
263
|
-
nested = false
|
|
264
|
-
} = options;
|
|
265
|
-
const baseFields = [
|
|
266
|
-
{
|
|
267
|
-
name: "title",
|
|
268
|
-
type: "text",
|
|
269
|
-
required: true,
|
|
270
|
-
localized: true
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
type: "tabs",
|
|
274
|
-
tabs: [
|
|
275
|
-
{
|
|
276
|
-
fields: [
|
|
277
|
-
{
|
|
278
|
-
name: "blocks",
|
|
279
|
-
type: "blocks",
|
|
280
|
-
blocks,
|
|
281
|
-
required: true,
|
|
282
|
-
admin: { initCollapsed: true }
|
|
283
|
-
}
|
|
284
|
-
],
|
|
285
|
-
label: "Content"
|
|
286
|
-
}
|
|
287
|
-
]
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
name: "publishedAt",
|
|
291
|
-
type: "date",
|
|
292
|
-
admin: { position: "sidebar" }
|
|
293
|
-
},
|
|
294
|
-
slugField({ nested })
|
|
295
|
-
];
|
|
296
|
-
const { afterChange: revalidateAfterChange, afterDelete: revalidateAfterDelete } = createRevalidateCollectionHook({
|
|
297
|
-
collectionSlug,
|
|
298
|
-
urlPathPrefix: "",
|
|
299
|
-
localePrefix,
|
|
300
|
-
defaultLocale
|
|
301
|
-
});
|
|
302
|
-
return {
|
|
303
|
-
slug: collectionSlug,
|
|
304
|
-
custom: { path: renderPath },
|
|
305
|
-
access: {
|
|
306
|
-
create: authenticated,
|
|
307
|
-
delete: authenticated,
|
|
308
|
-
read: authenticatedOrPublished,
|
|
309
|
-
update: authenticated
|
|
310
|
-
},
|
|
311
|
-
fields: baseFields,
|
|
312
|
-
hooks: {
|
|
313
|
-
afterChange: [revalidateAfterChange],
|
|
314
|
-
afterDelete: [revalidateAfterDelete]
|
|
315
|
-
},
|
|
316
|
-
versions: { drafts: { autosave: { interval: 1000 } } }
|
|
317
|
-
};
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
// src/data/collections/Posts/index.ts
|
|
321
|
-
var POSTS_SLUG2 = "posts";
|
|
322
|
-
var createPostsCollection = (options = {}) => {
|
|
323
|
-
const {
|
|
324
|
-
renderPath = POSTS_RENDER_PATH,
|
|
325
|
-
slug: collectionSlug = POSTS_SLUG2,
|
|
326
|
-
localePrefix,
|
|
327
|
-
defaultLocale
|
|
328
|
-
} = options;
|
|
329
|
-
const baseFields = [
|
|
330
|
-
{
|
|
331
|
-
name: "title",
|
|
332
|
-
type: "text",
|
|
333
|
-
required: true,
|
|
334
|
-
localized: true
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
name: "excerpt",
|
|
338
|
-
type: "text",
|
|
339
|
-
required: false,
|
|
340
|
-
localized: true
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
name: "content",
|
|
344
|
-
type: "richText",
|
|
345
|
-
required: false,
|
|
346
|
-
localized: true
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
name: "publishedAt",
|
|
350
|
-
type: "date",
|
|
351
|
-
admin: { position: "sidebar" }
|
|
352
|
-
},
|
|
353
|
-
slugField()
|
|
354
|
-
];
|
|
355
|
-
const { afterChange, afterDelete } = createRevalidateCollectionHook({
|
|
356
|
-
collectionSlug,
|
|
357
|
-
urlPathPrefix: "/posts",
|
|
358
|
-
localePrefix,
|
|
359
|
-
defaultLocale
|
|
360
|
-
});
|
|
361
|
-
return {
|
|
362
|
-
slug: collectionSlug,
|
|
363
|
-
custom: { path: renderPath },
|
|
364
|
-
access: {
|
|
365
|
-
create: authenticated,
|
|
366
|
-
delete: authenticated,
|
|
367
|
-
read: authenticatedOrPublished,
|
|
368
|
-
update: authenticated
|
|
369
|
-
},
|
|
370
|
-
fields: baseFields,
|
|
371
|
-
hooks: {
|
|
372
|
-
afterChange: [afterChange],
|
|
373
|
-
afterDelete: [afterDelete]
|
|
374
|
-
},
|
|
375
|
-
versions: { drafts: { autosave: { interval: 1000 } } }
|
|
376
|
-
};
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
// src/data/collections/StaticPages/index.ts
|
|
380
|
-
var createStaticPagesCollection = (blocks) => {
|
|
381
|
-
const { afterChange, afterDelete } = createRevalidateCollectionHook({
|
|
382
|
-
collectionSlug: STATIC_PAGES_SLUG,
|
|
383
|
-
pathMode: "tag-only",
|
|
384
|
-
sitemapTag: "static-pages"
|
|
385
|
-
});
|
|
386
|
-
return {
|
|
387
|
-
slug: STATIC_PAGES_SLUG,
|
|
388
|
-
dbName: "sp",
|
|
389
|
-
admin: { group: "System", useAsTitle: "title" },
|
|
390
|
-
access: {
|
|
391
|
-
create: authenticated,
|
|
392
|
-
delete: authenticated,
|
|
393
|
-
read: authenticatedOrPublished,
|
|
394
|
-
update: authenticated
|
|
395
|
-
},
|
|
396
|
-
fields: [
|
|
397
|
-
{
|
|
398
|
-
name: "kind",
|
|
399
|
-
type: "select",
|
|
400
|
-
required: true,
|
|
401
|
-
unique: true,
|
|
402
|
-
index: true,
|
|
403
|
-
options: [
|
|
404
|
-
{ label: "Not found (404)", value: "not-found" },
|
|
405
|
-
{ label: "Server error (500)", value: "server-error" },
|
|
406
|
-
{ label: "Search empty", value: "search-empty" },
|
|
407
|
-
{ label: "Offline", value: "offline" }
|
|
408
|
-
],
|
|
409
|
-
admin: {
|
|
410
|
-
position: "sidebar",
|
|
411
|
-
description: "Which system page this row powers. One row per kind."
|
|
412
|
-
}
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
name: "title",
|
|
416
|
-
type: "text",
|
|
417
|
-
required: true,
|
|
418
|
-
localized: true,
|
|
419
|
-
admin: { description: "Admin-only label. Not rendered." }
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
type: "tabs",
|
|
423
|
-
tabs: [
|
|
424
|
-
{
|
|
425
|
-
fields: [
|
|
426
|
-
{
|
|
427
|
-
name: "blocks",
|
|
428
|
-
type: "blocks",
|
|
429
|
-
localized: true,
|
|
430
|
-
blocks,
|
|
431
|
-
admin: { initCollapsed: true },
|
|
432
|
-
dbName: "b"
|
|
433
|
-
}
|
|
434
|
-
],
|
|
435
|
-
label: "Content"
|
|
436
|
-
}
|
|
437
|
-
]
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
name: "publishedAt",
|
|
441
|
-
type: "date",
|
|
442
|
-
admin: { position: "sidebar" }
|
|
443
|
-
}
|
|
444
|
-
],
|
|
445
|
-
hooks: {
|
|
446
|
-
afterChange: [afterChange],
|
|
447
|
-
afterDelete: [afterDelete]
|
|
448
|
-
},
|
|
449
|
-
versions: { drafts: { autosave: { interval: 1000 } } }
|
|
450
|
-
};
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// src/core/fields/link.ts
|
|
454
|
-
var appearanceOptions = {
|
|
455
|
-
default: { label: "Default", value: "default" },
|
|
456
|
-
outline: { label: "Outline", value: "outline" }
|
|
457
|
-
};
|
|
458
|
-
var link = (options = {}) => {
|
|
459
|
-
const {
|
|
460
|
-
appearances,
|
|
461
|
-
disableLabel = false,
|
|
462
|
-
relationTo = [PAGES_SLUG],
|
|
463
|
-
localized = false,
|
|
464
|
-
overrides = {},
|
|
465
|
-
extraFields = []
|
|
466
|
-
} = options;
|
|
467
|
-
const result = {
|
|
468
|
-
name: "link",
|
|
469
|
-
type: "group",
|
|
470
|
-
admin: { hideGutter: true },
|
|
471
|
-
fields: [
|
|
472
|
-
{
|
|
473
|
-
type: "row",
|
|
474
|
-
fields: [
|
|
475
|
-
{
|
|
476
|
-
name: "type",
|
|
477
|
-
type: "radio",
|
|
478
|
-
admin: { layout: "horizontal", width: "50%" },
|
|
479
|
-
defaultValue: "reference",
|
|
480
|
-
options: [
|
|
481
|
-
{ label: "Internal link", value: "reference" },
|
|
482
|
-
{ label: "Custom URL", value: "custom" }
|
|
483
|
-
]
|
|
484
|
-
},
|
|
485
|
-
{
|
|
486
|
-
name: "newTab",
|
|
487
|
-
type: "checkbox",
|
|
488
|
-
admin: { style: { alignSelf: "flex-end" }, width: "50%" },
|
|
489
|
-
label: "Open in new tab"
|
|
490
|
-
}
|
|
491
|
-
]
|
|
492
|
-
}
|
|
493
|
-
]
|
|
494
|
-
};
|
|
495
|
-
const linkTypes = [
|
|
496
|
-
{
|
|
497
|
-
name: "reference",
|
|
498
|
-
type: "relationship",
|
|
499
|
-
admin: { condition: (_, siblingData) => siblingData?.type === "reference" },
|
|
500
|
-
label: "Document to link to",
|
|
501
|
-
relationTo,
|
|
502
|
-
required: true,
|
|
503
|
-
localized
|
|
504
|
-
},
|
|
505
|
-
{
|
|
506
|
-
name: "url",
|
|
507
|
-
type: "text",
|
|
508
|
-
admin: { condition: (_, siblingData) => siblingData?.type === "custom" },
|
|
509
|
-
label: "Custom URL",
|
|
510
|
-
required: true,
|
|
511
|
-
localized
|
|
512
|
-
}
|
|
513
|
-
];
|
|
514
|
-
if (!disableLabel) {
|
|
515
|
-
result.fields.push({
|
|
516
|
-
type: "row",
|
|
517
|
-
fields: [
|
|
518
|
-
...linkTypes,
|
|
519
|
-
{
|
|
520
|
-
name: "label",
|
|
521
|
-
type: "text",
|
|
522
|
-
admin: { width: "50%" },
|
|
523
|
-
label: "Label",
|
|
524
|
-
required: true,
|
|
525
|
-
localized
|
|
526
|
-
}
|
|
527
|
-
]
|
|
528
|
-
});
|
|
529
|
-
} else {
|
|
530
|
-
result.fields = [...result.fields, ...linkTypes];
|
|
531
|
-
}
|
|
532
|
-
if (appearances !== false) {
|
|
533
|
-
const opts = appearances ? appearances.map((a) => appearanceOptions[a]) : [appearanceOptions.default, appearanceOptions.outline];
|
|
534
|
-
result.fields.push({
|
|
535
|
-
name: "appearance",
|
|
536
|
-
type: "select",
|
|
537
|
-
admin: { description: "Choose how the link should be rendered." },
|
|
538
|
-
defaultValue: "default",
|
|
539
|
-
options: opts
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
if (extraFields.length) {
|
|
543
|
-
result.fields.push(...extraFields);
|
|
544
|
-
}
|
|
545
|
-
return { ...result, ...overrides };
|
|
546
|
-
};
|
|
547
|
-
|
|
548
|
-
// src/render/hooks/revalidateGlobal.ts
|
|
549
|
-
function createRevalidateGlobalHook(slug) {
|
|
550
|
-
console.log("[WWW] render/hooks:createRevalidateGlobalHook slug=", slug);
|
|
551
|
-
return async ({ doc, req: { payload, context, locale } }) => {
|
|
552
|
-
if (shouldSkipRevalidate(context))
|
|
553
|
-
return doc;
|
|
554
|
-
const tags = [`global_${slug}`, `global_${slug}_${locale}`];
|
|
555
|
-
console.log("[WWW] render/hooks:revalidateGlobal slug=", slug, "locale=", locale, "tags=", JSON.stringify(tags));
|
|
556
|
-
payload.logger.info?.(`Revalidating global: ${tags.join(", ")}`);
|
|
557
|
-
for (const tag of tags) {
|
|
558
|
-
await safeRevalidateTag(payload, tag);
|
|
559
|
-
}
|
|
560
|
-
return doc;
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// src/data/collections/globals/Header/config.ts
|
|
565
|
-
var createHeaderGlobal = (options = {}) => {
|
|
566
|
-
const {
|
|
567
|
-
renderPath = HEADER_RENDER_PATH,
|
|
568
|
-
linkRelationTo = [PAGES_SLUG],
|
|
569
|
-
navColumnLinkFields = [],
|
|
570
|
-
navItemLinkFields = []
|
|
571
|
-
} = options;
|
|
572
|
-
const navColumnBlock = {
|
|
573
|
-
slug: "navColumn",
|
|
574
|
-
fields: [
|
|
575
|
-
{ name: "title", type: "text", required: true, localized: true },
|
|
576
|
-
{ name: "links", type: "array", fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navColumnLinkFields })] }
|
|
577
|
-
]
|
|
578
|
-
};
|
|
579
|
-
const navItemBlock = {
|
|
580
|
-
slug: "navItem",
|
|
581
|
-
fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navItemLinkFields })]
|
|
582
|
-
};
|
|
583
|
-
return {
|
|
584
|
-
slug: "header",
|
|
585
|
-
custom: { path: renderPath },
|
|
586
|
-
access: { read: () => true },
|
|
587
|
-
fields: [
|
|
588
|
-
{
|
|
589
|
-
name: "nav",
|
|
590
|
-
type: "blocks",
|
|
591
|
-
required: true,
|
|
592
|
-
blocks: [navColumnBlock, navItemBlock]
|
|
593
|
-
}
|
|
594
|
-
],
|
|
595
|
-
hooks: {
|
|
596
|
-
afterChange: [createRevalidateGlobalHook("header")]
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
// src/data/collections/globals/Footer/config.ts
|
|
602
|
-
var createFooterGlobal = (options = {}) => {
|
|
603
|
-
const {
|
|
604
|
-
renderPath = FOOTER_RENDER_PATH,
|
|
605
|
-
linkRelationTo = [PAGES_SLUG],
|
|
606
|
-
navColumnLinkFields = [],
|
|
607
|
-
navItemLinkFields = []
|
|
608
|
-
} = options;
|
|
609
|
-
const navColumnBlock = {
|
|
610
|
-
slug: "navColumn",
|
|
611
|
-
fields: [
|
|
612
|
-
{ name: "title", type: "text", required: true, localized: true },
|
|
613
|
-
{ name: "links", type: "array", fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navColumnLinkFields })] }
|
|
614
|
-
]
|
|
615
|
-
};
|
|
616
|
-
const navItemBlock = {
|
|
617
|
-
slug: "navItem",
|
|
618
|
-
fields: [link({ appearances: false, localized: true, relationTo: linkRelationTo, extraFields: navItemLinkFields })]
|
|
619
|
-
};
|
|
620
|
-
return {
|
|
621
|
-
slug: "footer",
|
|
622
|
-
custom: { path: renderPath },
|
|
623
|
-
access: { read: () => true },
|
|
624
|
-
fields: [
|
|
625
|
-
{
|
|
626
|
-
name: "nav",
|
|
627
|
-
type: "blocks",
|
|
628
|
-
required: true,
|
|
629
|
-
blocks: [navColumnBlock, navItemBlock]
|
|
630
|
-
}
|
|
631
|
-
],
|
|
632
|
-
hooks: {
|
|
633
|
-
afterChange: [createRevalidateGlobalHook("footer")]
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
};
|
|
637
|
-
|
|
638
|
-
// src/config/createWWWConfig.ts
|
|
639
|
-
import openAIResolver from "@justanarthur/payload-plugin-translator/resolvers/openAI";
|
|
640
|
-
|
|
641
|
-
// src/render/preview/createPreviewHandler.ts
|
|
642
|
-
function createPreviewHandler(options) {
|
|
643
|
-
const { getServerSideURL: _getServerSideURL, secret, enableDraftMode = true } = options;
|
|
644
|
-
return async function GET(req) {
|
|
645
|
-
const { draftMode } = await import("next/headers");
|
|
646
|
-
const { redirect } = await import("next/navigation");
|
|
647
|
-
const url = new URL(req.url);
|
|
648
|
-
const path = url.searchParams.get("path") ?? "/";
|
|
649
|
-
const previewSecret = url.searchParams.get("previewSecret");
|
|
650
|
-
console.log("[WWW] render/preview:createPreviewHandler:GET path=", path, "hasSecret=", Boolean(previewSecret), "enableDraftMode=", enableDraftMode);
|
|
651
|
-
if (!previewSecret || previewSecret !== secret) {
|
|
652
|
-
console.warn("[WWW] render/preview:createPreviewHandler:GET invalid preview secret (401)");
|
|
653
|
-
return new Response("Invalid preview secret", { status: 401 });
|
|
654
|
-
}
|
|
655
|
-
if (enableDraftMode) {
|
|
656
|
-
(await draftMode()).enable();
|
|
657
|
-
console.log("[WWW] render/preview:createPreviewHandler:GET draftMode enabled");
|
|
658
|
-
}
|
|
659
|
-
redirect(path);
|
|
660
|
-
return new Response(null, { status: 204 });
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
// src/render/metadata/hreflang.ts
|
|
664
|
-
async function buildHreflangAlternates({
|
|
665
|
-
siteUrl,
|
|
666
|
-
locale,
|
|
667
|
-
urlPrefix,
|
|
668
|
-
storedSlug,
|
|
669
|
-
queryAllLocaleSlugs,
|
|
670
|
-
nested,
|
|
671
|
-
homeSlug,
|
|
672
|
-
defaultLocale,
|
|
673
|
-
locales,
|
|
674
|
-
localePrefix = "always"
|
|
675
|
-
}) {
|
|
676
|
-
const allLocaleSlugs = await queryAllLocaleSlugs(storedSlug, locale);
|
|
677
|
-
console.log("[WWW] render/metadata:buildHreflangAlternates storedSlug=", storedSlug, "locale=", locale, "allLocaleSlugs=", JSON.stringify(allLocaleSlugs));
|
|
678
|
-
const languages = {};
|
|
679
|
-
const urlFor = (l, slug) => {
|
|
680
|
-
const trimmedPrefix = urlPrefix.replace(/^\/|\/$/g, "");
|
|
681
|
-
const prefixSegment = trimmedPrefix ? `/${trimmedPrefix}` : "";
|
|
682
|
-
const urlPath = slug === homeSlug ? "/" : nested ? "/" + slug.replaceAll("_", "/") : "/" + slug;
|
|
683
|
-
const localeSegment = localePrefix === "never" ? "" : localePrefix === "as-needed" && l === defaultLocale ? "" : `/${l}`;
|
|
684
|
-
return `${siteUrl}${localeSegment}${prefixSegment}${urlPath}`;
|
|
685
|
-
};
|
|
686
|
-
for (const l of locales) {
|
|
687
|
-
const slugForLocale = allLocaleSlugs?.[l];
|
|
688
|
-
if (!slugForLocale)
|
|
689
|
-
continue;
|
|
690
|
-
languages[l] = urlFor(l, slugForLocale);
|
|
691
|
-
}
|
|
692
|
-
if (allLocaleSlugs?.[defaultLocale]) {
|
|
693
|
-
languages["x-default"] = urlFor(defaultLocale, allLocaleSlugs[defaultLocale]);
|
|
694
|
-
}
|
|
695
|
-
console.log("[WWW] render/metadata:buildHreflangAlternates ->", JSON.stringify(languages));
|
|
696
|
-
return languages;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// src/render/metadata/query.ts
|
|
700
|
-
import { getPayload } from "payload";
|
|
701
|
-
import { cache } from "react";
|
|
702
|
-
|
|
703
|
-
// src/core/utils/getFromImportMap.ts
|
|
704
|
-
function getFromImportMap(key, importMap) {
|
|
705
|
-
return importMap[key.includes("#") ? key : key + "#default"];
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// src/render/metadata/query.ts
|
|
709
|
-
var queryDocBySlug = cache(async function queryDocBySlug2({
|
|
710
|
-
collectionSlug,
|
|
711
|
-
slug,
|
|
712
|
-
slugField: slugField2 = "slug",
|
|
713
|
-
locale,
|
|
714
|
-
draft = false,
|
|
715
|
-
config
|
|
716
|
-
}) {
|
|
717
|
-
console.log("[WWW] render/metadata:queryDocBySlug collection=", collectionSlug, "slug=", slug, "slugField=", slugField2, "locale=", locale, "draft=", draft);
|
|
718
|
-
const payload = await getPayload({ config });
|
|
719
|
-
const result = await payload.find({
|
|
720
|
-
collection: collectionSlug,
|
|
721
|
-
draft,
|
|
722
|
-
limit: 1,
|
|
723
|
-
pagination: false,
|
|
724
|
-
overrideAccess: draft,
|
|
725
|
-
where: { [slugField2]: { equals: slug } },
|
|
726
|
-
locale
|
|
727
|
-
});
|
|
728
|
-
const doc = result.docs?.[0] ?? null;
|
|
729
|
-
console.log("[WWW] render/metadata:queryDocBySlug ->", doc ? "hit" : "miss");
|
|
730
|
-
return doc;
|
|
731
|
-
});
|
|
732
|
-
var queryAllDocs = cache(async function queryAllDocs2({
|
|
733
|
-
collectionSlug,
|
|
734
|
-
slugField: slugField2 = "slug",
|
|
735
|
-
locale,
|
|
736
|
-
config
|
|
737
|
-
}) {
|
|
738
|
-
console.log("[WWW] render/metadata:queryAllDocs collection=", collectionSlug, "locale=", locale);
|
|
739
|
-
const payload = await getPayload({ config });
|
|
740
|
-
const result = await payload.find({
|
|
741
|
-
collection: collectionSlug,
|
|
742
|
-
draft: false,
|
|
743
|
-
limit: 1000,
|
|
744
|
-
pagination: false,
|
|
745
|
-
overrideAccess: false,
|
|
746
|
-
select: { [slugField2]: true },
|
|
747
|
-
locale
|
|
748
|
-
});
|
|
749
|
-
const docs = result.docs ?? [];
|
|
750
|
-
console.log("[WWW] render/metadata:queryAllDocs -> count=", docs.length);
|
|
751
|
-
return docs;
|
|
752
|
-
});
|
|
753
|
-
var queryAllLocaleSlugs = cache(async function queryAllLocaleSlugs2({
|
|
754
|
-
collectionSlug,
|
|
755
|
-
slug,
|
|
756
|
-
slugField: slugField2 = "slug",
|
|
757
|
-
locale,
|
|
758
|
-
config
|
|
759
|
-
}) {
|
|
760
|
-
console.log("[WWW] render/metadata:queryAllLocaleSlugs collection=", collectionSlug, "slug=", slug, "locale=", locale);
|
|
761
|
-
const payload = await getPayload({ config });
|
|
762
|
-
const result = await payload.find({
|
|
763
|
-
collection: collectionSlug,
|
|
764
|
-
draft: false,
|
|
765
|
-
limit: 1,
|
|
766
|
-
pagination: false,
|
|
767
|
-
overrideAccess: false,
|
|
768
|
-
locale,
|
|
769
|
-
where: { [slugField2]: { equals: slug } },
|
|
770
|
-
select: { [slugField2]: true }
|
|
771
|
-
});
|
|
772
|
-
const doc = result.docs?.[0];
|
|
773
|
-
if (!doc) {
|
|
774
|
-
console.log("[WWW] render/metadata:queryAllLocaleSlugs -> undefined (no doc)");
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
let fieldValue = doc[slugField2];
|
|
778
|
-
if (doc.id != null) {
|
|
779
|
-
const allLocales2 = await payload.findByID({
|
|
780
|
-
collection: collectionSlug,
|
|
781
|
-
id: doc.id,
|
|
782
|
-
locale: "all",
|
|
783
|
-
draft: false,
|
|
784
|
-
overrideAccess: false,
|
|
785
|
-
select: { [slugField2]: true }
|
|
786
|
-
}).catch(() => null);
|
|
787
|
-
if (allLocales2)
|
|
788
|
-
fieldValue = allLocales2[slugField2];
|
|
789
|
-
}
|
|
790
|
-
if (fieldValue && typeof fieldValue === "object") {
|
|
791
|
-
console.log("[WWW] render/metadata:queryAllLocaleSlugs -> localized map=", JSON.stringify(fieldValue));
|
|
792
|
-
return fieldValue;
|
|
793
|
-
}
|
|
794
|
-
const resolved = await config;
|
|
795
|
-
const rawLocales = Array.isArray(resolved?.localization?.localeCodes) ? resolved.localization.localeCodes : resolved?.localization?.locales?.map((l) => l.code) ?? [];
|
|
796
|
-
const out = {};
|
|
797
|
-
for (const l of rawLocales)
|
|
798
|
-
out[l] = String(fieldValue ?? slug);
|
|
799
|
-
console.log("[WWW] render/metadata:queryAllLocaleSlugs -> flat-fanout locales=", rawLocales.length, "slug=", JSON.stringify(fieldValue ?? slug));
|
|
800
|
-
return out;
|
|
801
|
-
});
|
|
802
|
-
var queryGlobal = cache(async function queryGlobal2({
|
|
803
|
-
globalSlug,
|
|
804
|
-
locale,
|
|
805
|
-
depth = 0,
|
|
806
|
-
draft = false,
|
|
807
|
-
config
|
|
808
|
-
}) {
|
|
809
|
-
console.log("[WWW] render/metadata:queryGlobal global=", globalSlug, "locale=", locale, "depth=", depth, "draft=", draft);
|
|
810
|
-
const payload = await getPayload({ config });
|
|
811
|
-
try {
|
|
812
|
-
const global = await payload.findGlobal({
|
|
813
|
-
slug: globalSlug,
|
|
814
|
-
depth,
|
|
815
|
-
draft,
|
|
816
|
-
locale
|
|
817
|
-
});
|
|
818
|
-
console.log("[WWW] render/metadata:queryGlobal -> hit");
|
|
819
|
-
return global;
|
|
820
|
-
} catch (error) {
|
|
821
|
-
console.warn("[WWW] render/metadata:queryGlobal failed for slug=", globalSlug, "err=", String(error));
|
|
822
|
-
return null;
|
|
823
|
-
}
|
|
824
|
-
});
|
|
825
|
-
function getRenderModuleExports(exportName, collection, importMap) {
|
|
826
|
-
const path = collection?.custom?.path;
|
|
827
|
-
if (!path) {
|
|
828
|
-
console.log("[WWW] render/metadata:getRenderModuleExports no custom.path exportName=", exportName);
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
const mod = getFromImportMap(path, importMap);
|
|
832
|
-
const value = mod?.[exportName];
|
|
833
|
-
console.log("[WWW] render/metadata:getRenderModuleExports exportName=", exportName, "path=", path, "hit=", Boolean(value));
|
|
834
|
-
return value;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
// src/render/sitemap/createSitemapFile.ts
|
|
838
|
-
function createSitemapFile(options) {
|
|
839
|
-
const { collections } = options;
|
|
840
|
-
return async function sitemap() {
|
|
841
|
-
console.log("[WWW] render/sitemap:createSitemapFile:sitemap collections=", JSON.stringify(options.collections), "localePrefix=", options.localePrefix ?? "always");
|
|
842
|
-
const cfg = await options.config;
|
|
843
|
-
const allLocales2 = Array.isArray(cfg.localization?.locales) ? cfg.localization.locales.map((l) => typeof l === "string" ? l : l.code) : [cfg.localization?.defaultLocale ?? "en"];
|
|
844
|
-
const defaultLocale = cfg.localization?.defaultLocale ?? allLocales2[0];
|
|
845
|
-
const activeLocales = Array.isArray(options.locales) && options.locales.length > 0 ? options.locales.filter((l) => allLocales2.includes(l)) : allLocales2;
|
|
846
|
-
console.log("[WWW] render/sitemap:createSitemapFile:sitemap allLocales=", JSON.stringify(allLocales2), "default=", defaultLocale, "active=", JSON.stringify(activeLocales));
|
|
847
|
-
const entries = [];
|
|
848
|
-
const seen = new Set;
|
|
849
|
-
for (const collectionSlug of collections) {
|
|
850
|
-
const collectionDefaults = options.perCollection?.[collectionSlug] ?? {};
|
|
851
|
-
const urlPrefix = (options.urlPrefixes?.[collectionSlug] ?? "").replace(/\/$/, "");
|
|
852
|
-
const siteUrl = options.getServerSideURL().replace(/\/$/, "");
|
|
853
|
-
const isNested = options.nested?.[collectionSlug] ?? false;
|
|
854
|
-
for (const locale of activeLocales) {
|
|
855
|
-
const docs = await queryAllDocs({
|
|
856
|
-
collectionSlug,
|
|
857
|
-
slugField: "slug",
|
|
858
|
-
locale,
|
|
859
|
-
config: options.config
|
|
860
|
-
});
|
|
861
|
-
for (const doc of docs) {
|
|
862
|
-
const storedSlug = doc.slug;
|
|
863
|
-
if (typeof storedSlug !== "string" || storedSlug === "")
|
|
864
|
-
continue;
|
|
865
|
-
const dedupeKey = `${collectionSlug}:${locale}:${storedSlug}`;
|
|
866
|
-
if (seen.has(dedupeKey))
|
|
867
|
-
continue;
|
|
868
|
-
seen.add(dedupeKey);
|
|
869
|
-
const slugPath = isNested ? storedSlug.replaceAll("_", "/") : storedSlug;
|
|
870
|
-
const url = `${siteUrl}${prefixFor(locale, defaultLocale, options.localePrefix ?? "always")}${urlPrefix}/${slugPath}`;
|
|
871
|
-
const languages = await buildHreflangAlternates({
|
|
872
|
-
siteUrl,
|
|
873
|
-
locale,
|
|
874
|
-
urlPrefix,
|
|
875
|
-
storedSlug,
|
|
876
|
-
nested: isNested,
|
|
877
|
-
homeSlug: "",
|
|
878
|
-
defaultLocale,
|
|
879
|
-
locales: activeLocales,
|
|
880
|
-
localePrefix: options.localePrefix,
|
|
881
|
-
queryAllLocaleSlugs: async (s, l) => {
|
|
882
|
-
const result = await queryAllLocaleSlugs({
|
|
883
|
-
collectionSlug,
|
|
884
|
-
slug: s,
|
|
885
|
-
slugField: "slug",
|
|
886
|
-
locale: l,
|
|
887
|
-
config: options.config
|
|
888
|
-
});
|
|
889
|
-
return result ?? undefined;
|
|
890
|
-
}
|
|
891
|
-
});
|
|
892
|
-
entries.push({
|
|
893
|
-
url,
|
|
894
|
-
lastModified: doc.updatedAt ? new Date(doc.updatedAt) : undefined,
|
|
895
|
-
changeFrequency: collectionDefaults.changefreq ?? "weekly",
|
|
896
|
-
priority: collectionDefaults.priority ?? 0.5,
|
|
897
|
-
alternates: { languages }
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
console.log("[WWW] render/sitemap:createSitemapFile:sitemap -> entries=", entries.length);
|
|
903
|
-
return entries;
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
// src/config/createWWWConfig.ts
|
|
907
|
-
function createWWWConfig(options) {
|
|
908
|
-
const { locales, routing, blocks, linkRelationTo, registerPosts = true, nested = false, defaultPlugins } = options;
|
|
909
|
-
const defaultLocale = routing?.defaultLocale ?? locales[0] ?? "";
|
|
910
|
-
const localePrefixMode = (() => {
|
|
911
|
-
const raw = routing?.localePrefix;
|
|
912
|
-
if (typeof raw === "string")
|
|
913
|
-
return raw;
|
|
914
|
-
return raw?.mode ?? "always";
|
|
915
|
-
})();
|
|
916
|
-
if (locales.length === 0) {
|
|
917
|
-
throw new Error("createWWWConfig: `locales` must contain at least one entry.");
|
|
918
|
-
}
|
|
919
|
-
const buildPagesCollection = () => createPagesCollection(blocks, {
|
|
920
|
-
localePrefix: localePrefixMode,
|
|
921
|
-
defaultLocale,
|
|
922
|
-
nested
|
|
923
|
-
});
|
|
924
|
-
const buildPostsCollection = () => createPostsCollection({
|
|
925
|
-
localePrefix: localePrefixMode,
|
|
926
|
-
defaultLocale
|
|
927
|
-
});
|
|
928
|
-
const buildStaticPagesCollection = () => createStaticPagesCollection(blocks);
|
|
929
|
-
const buildHeaderGlobal = () => createHeaderGlobal({ linkRelationTo });
|
|
930
|
-
const buildFooterGlobal = () => createFooterGlobal({ linkRelationTo });
|
|
931
|
-
async function withWWWConfig(config) {
|
|
932
|
-
const baseDefaults = [
|
|
933
|
-
buildPagesCollection(),
|
|
934
|
-
buildStaticPagesCollection()
|
|
935
|
-
];
|
|
936
|
-
if (registerPosts)
|
|
937
|
-
baseDefaults.push(buildPostsCollection());
|
|
938
|
-
const defaultCollections = baseDefaults;
|
|
939
|
-
const collections = typeof config.collections === "function" ? config.collections({ defaultCollections }) : [...defaultCollections, ...config.collections || []];
|
|
940
|
-
const defaultGlobals = [buildHeaderGlobal(), buildFooterGlobal()];
|
|
941
|
-
const globals = typeof config.globals === "function" ? config.globals({ defaultGlobals }) : [...defaultGlobals, ...config.globals || []];
|
|
942
|
-
const renderDependencies = {
|
|
943
|
-
[PAGES_RENDER_PATH]: { path: PAGES_RENDER_PATH, type: "component" },
|
|
944
|
-
[POSTS_RENDER_PATH]: { path: POSTS_RENDER_PATH, type: "component" },
|
|
945
|
-
[HEADER_RENDER_PATH]: { path: HEADER_RENDER_PATH, type: "component" },
|
|
946
|
-
[FOOTER_RENDER_PATH]: { path: FOOTER_RENDER_PATH, type: "component" },
|
|
947
|
-
[LIVE_PREVIEW_LISTENER_PATH]: {
|
|
948
|
-
path: LIVE_PREVIEW_LISTENER_PATH,
|
|
949
|
-
type: "component"
|
|
950
|
-
}
|
|
951
|
-
};
|
|
952
|
-
for (const { slug, custom, admin } of blocks) {
|
|
953
|
-
const path = custom?.path ?? admin?.custom?.path;
|
|
954
|
-
if (typeof path === "string" && slug)
|
|
955
|
-
renderDependencies[slug] = { path, type: "component" };
|
|
956
|
-
}
|
|
957
|
-
for (const entry of [...collections, ...globals]) {
|
|
958
|
-
const path = entry.custom?.path;
|
|
959
|
-
if (typeof path === "string")
|
|
960
|
-
renderDependencies[path] = { path, type: "component" };
|
|
961
|
-
}
|
|
962
|
-
const [
|
|
963
|
-
{ seoPlugin },
|
|
964
|
-
{ imageHashPlugin },
|
|
965
|
-
{ translator }
|
|
966
|
-
] = await Promise.all([
|
|
967
|
-
import("@justanarthur/payload-plugin-seo"),
|
|
968
|
-
import("@justanarthur/payload-imagehash-plugin"),
|
|
969
|
-
import("@justanarthur/payload-plugin-translator")
|
|
970
|
-
]);
|
|
971
|
-
const defaultPluginList = [
|
|
972
|
-
seoPlugin({
|
|
973
|
-
collections: ["pages", "posts", "static-pages"],
|
|
974
|
-
autoGenerate: { mode: "onCreate", deriveFrom: "allScalars" }
|
|
975
|
-
}),
|
|
976
|
-
imageHashPlugin({ algorithm: "lqip-modern" }),
|
|
977
|
-
translator({
|
|
978
|
-
collections: ["pages", "posts", "static-pages"],
|
|
979
|
-
globals: ["header", "footer"],
|
|
980
|
-
resolvers: [
|
|
981
|
-
openAIResolver({
|
|
982
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
983
|
-
chunkLength: 31,
|
|
984
|
-
model: "gpt-5-mini"
|
|
985
|
-
})
|
|
986
|
-
]
|
|
987
|
-
})
|
|
988
|
-
];
|
|
989
|
-
const mergedPlugins = defaultPlugins ? defaultPlugins(defaultPluginList) : defaultPluginList;
|
|
990
|
-
return {
|
|
991
|
-
...config,
|
|
992
|
-
collections,
|
|
993
|
-
globals,
|
|
994
|
-
plugins: mergedPlugins,
|
|
995
|
-
admin: {
|
|
996
|
-
...config.admin ?? {},
|
|
997
|
-
dependencies: {
|
|
998
|
-
...renderDependencies,
|
|
999
|
-
...config.admin?.dependencies ?? {}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
return { withWWWConfig };
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
// src/data/test/createTestPayload.ts
|
|
1008
|
-
async function createTestPayload(options = {}) {
|
|
1009
|
-
const filename = fileURLToPath(import.meta.url);
|
|
1010
|
-
const dirname = path.dirname(filename);
|
|
1011
|
-
const databaseFile = options.databaseFile ?? path.join(dirname, `.test-${Date.now()}-${Math.random().toString(36).slice(2)}.db`);
|
|
1012
|
-
const locales = options.locales ?? ["en", "uk"];
|
|
1013
|
-
const defaultLocale = locales[0] ?? "en";
|
|
1014
|
-
const blocks = options.blocks ?? [];
|
|
1015
|
-
const { withWWWConfig } = createWWWConfig({ locales: [...locales], blocks });
|
|
1016
|
-
const config = await withWWWConfig({
|
|
1017
|
-
admin: { user: "users" },
|
|
1018
|
-
blocks: [],
|
|
1019
|
-
collections: [
|
|
1020
|
-
{
|
|
1021
|
-
slug: "posts",
|
|
1022
|
-
access: { read: () => true },
|
|
1023
|
-
fields: [{ name: "title", type: "text" }]
|
|
1024
|
-
}
|
|
1025
|
-
],
|
|
1026
|
-
globals: [],
|
|
1027
|
-
db: sqliteAdapter({
|
|
1028
|
-
client: { url: `file:${databaseFile}` },
|
|
1029
|
-
push: false
|
|
1030
|
-
}),
|
|
1031
|
-
secret: "test-secret-do-not-use-in-prod",
|
|
1032
|
-
sharp: (await import("sharp")).default,
|
|
1033
|
-
editor: {},
|
|
1034
|
-
localization: { locales: [...locales], defaultLocale },
|
|
1035
|
-
kv: inMemoryKVAdapter(),
|
|
1036
|
-
typescript: { outputFile: "./payload-types.ts" }
|
|
1037
|
-
});
|
|
1038
|
-
const registered = new Set([
|
|
1039
|
-
...(config.collections ?? []).map((c) => c.slug),
|
|
1040
|
-
...(config.globals ?? []).map((g) => g.slug)
|
|
1041
|
-
]);
|
|
1042
|
-
const findMissing = (fields) => {
|
|
1043
|
-
const out = [];
|
|
1044
|
-
const walk = (f) => {
|
|
1045
|
-
if (Array.isArray(f)) {
|
|
1046
|
-
for (const child of f)
|
|
1047
|
-
walk(child);
|
|
1048
|
-
return;
|
|
1049
|
-
}
|
|
1050
|
-
if (f.relationTo) {
|
|
1051
|
-
const rels = Array.isArray(f.relationTo) ? f.relationTo : [f.relationTo];
|
|
1052
|
-
for (const r of rels) {
|
|
1053
|
-
if (typeof r === "string" && !registered.has(r))
|
|
1054
|
-
out.push(r);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
if (Array.isArray(f.fields))
|
|
1058
|
-
walk(f.fields);
|
|
1059
|
-
};
|
|
1060
|
-
for (const f of fields)
|
|
1061
|
-
walk(f);
|
|
1062
|
-
return out;
|
|
1063
|
-
};
|
|
1064
|
-
for (const c of config.collections ?? []) {
|
|
1065
|
-
const missing = findMissing(c.fields ?? []);
|
|
1066
|
-
if (missing.length)
|
|
1067
|
-
console.log("collection", c.slug, "missing relations:", missing);
|
|
1068
|
-
}
|
|
1069
|
-
for (const g of config.globals ?? []) {
|
|
1070
|
-
const missing = findMissing(g.fields ?? []);
|
|
1071
|
-
if (missing.length)
|
|
1072
|
-
console.log("global", g.slug, "missing relations:", missing);
|
|
1073
|
-
}
|
|
1074
|
-
for (const c of config.collections ?? []) {
|
|
1075
|
-
if (c.fields) {
|
|
1076
|
-
c.flattenedFields = flattenAllFields({ fields: c.fields });
|
|
1077
|
-
}
|
|
1078
|
-
if (c.upload === undefined)
|
|
1079
|
-
c.upload = false;
|
|
1080
|
-
c.sanitizedIndexes = c.sanitizedIndexes ?? [];
|
|
1081
|
-
c.polymorphicJoins = c.polymorphicJoins ?? [];
|
|
1082
|
-
if (options.overridePagesVersions && c.slug === "pages") {
|
|
1083
|
-
c.versions = false;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
for (const g of config.globals ?? []) {
|
|
1087
|
-
if (g.fields) {
|
|
1088
|
-
g.flattenedFields = flattenAllFields({ fields: g.fields });
|
|
1089
|
-
}
|
|
1090
|
-
g.sanitizedIndexes = g.sanitizedIndexes ?? [];
|
|
1091
|
-
}
|
|
1092
|
-
const payload = await getPayload2({ config });
|
|
1093
|
-
let destroyed = false;
|
|
1094
|
-
const destroy = async () => {
|
|
1095
|
-
if (destroyed)
|
|
1096
|
-
return;
|
|
1097
|
-
destroyed = true;
|
|
1098
|
-
try {
|
|
1099
|
-
await payload.destroy();
|
|
1100
|
-
} catch {}
|
|
1101
|
-
};
|
|
1102
|
-
return { payload, databaseFile, destroy };
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
// src/exports/data-test.ts
|
|
1106
|
-
var data_test_default = createTestPayload;
|
|
1107
|
-
export {
|
|
1108
|
-
data_test_default as default,
|
|
1109
|
-
createTestPayload
|
|
1110
|
-
};
|