@asteroidcms/core-utils 0.1.7 → 0.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 +168 -1
- package/dist/client.cjs +505 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +309 -1
- package/dist/client.d.ts +309 -1
- package/dist/client.js +500 -3
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +379 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +186 -1
- package/dist/index.d.ts +186 -1
- package/dist/index.js +368 -1
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +211 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +114 -0
- package/dist/next.d.ts +114 -0
- package/dist/next.js +201 -0
- package/dist/next.js.map +1 -0
- package/dist/server.cjs +699 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +342 -0
- package/dist/server.d.ts +342 -0
- package/dist/server.js +669 -0
- package/dist/server.js.map +1 -0
- package/package.json +23 -2
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('server-only');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var client$1 = require('@apollo/client');
|
|
6
|
+
var context = require('@apollo/client/link/context');
|
|
7
|
+
var error = require('@apollo/client/link/error');
|
|
8
|
+
var client = require('@asteroidcms/core-utils/client');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
|
|
11
|
+
function _interopNamespace(e) {
|
|
12
|
+
if (e && e.__esModule) return e;
|
|
13
|
+
var n = Object.create(null);
|
|
14
|
+
if (e) {
|
|
15
|
+
Object.keys(e).forEach(function (k) {
|
|
16
|
+
if (k !== 'default') {
|
|
17
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
18
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return e[k]; }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
n.default = e;
|
|
26
|
+
return Object.freeze(n);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
30
|
+
|
|
31
|
+
// src/server.ts
|
|
32
|
+
function createErrorLink(onError) {
|
|
33
|
+
return new error.ErrorLink(({ error }) => {
|
|
34
|
+
if (!onError) return;
|
|
35
|
+
if (client$1.CombinedGraphQLErrors.is(error)) {
|
|
36
|
+
error.errors.forEach((e) => onError(e));
|
|
37
|
+
} else if (client$1.CombinedProtocolErrors.is(error)) {
|
|
38
|
+
error.errors.forEach((e) => onError(e));
|
|
39
|
+
} else {
|
|
40
|
+
onError(error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/apollo/createApolloClient.ts
|
|
46
|
+
var trimTrailingSlash = (s) => s.replace(/\/+$/, "");
|
|
47
|
+
var ensureLeadingSlash = (s) => s.startsWith("/") ? s : `/${s}`;
|
|
48
|
+
function resolveConfig(config) {
|
|
49
|
+
if (!config.cmsUrl) throw new Error("AsteroidCMSProvider: `cmsUrl` is required.");
|
|
50
|
+
if (!config.apiKey) throw new Error("AsteroidCMSProvider: `apiKey` is required.");
|
|
51
|
+
return {
|
|
52
|
+
cmsUrl: trimTrailingSlash(config.cmsUrl),
|
|
53
|
+
apiKey: config.apiKey,
|
|
54
|
+
graphqlPath: ensureLeadingSlash(config.graphqlPath ?? "/graphql"),
|
|
55
|
+
mediaPath: ensureLeadingSlash(config.mediaPath ?? "/media/canonical"),
|
|
56
|
+
headers: config.headers ?? {},
|
|
57
|
+
onError: config.onError
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function createApolloClient(config) {
|
|
61
|
+
if (config.client) return config.client;
|
|
62
|
+
const resolved = resolveConfig(config);
|
|
63
|
+
const uri = `${resolved.cmsUrl}${resolved.graphqlPath}`;
|
|
64
|
+
const authLink = new context.SetContextLink((_op, prevContext) => {
|
|
65
|
+
const prevHeaders = prevContext.headers ?? {};
|
|
66
|
+
return {
|
|
67
|
+
...prevContext,
|
|
68
|
+
headers: {
|
|
69
|
+
...prevHeaders,
|
|
70
|
+
"x-api-key": resolved.apiKey,
|
|
71
|
+
...resolved.headers
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
const httpLink = new client$1.HttpLink({ uri });
|
|
76
|
+
return new client$1.ApolloClient({
|
|
77
|
+
link: client$1.ApolloLink.from([createErrorLink(resolved.onError), authLink, httpLink]),
|
|
78
|
+
cache: new client$1.InMemoryCache(config.cacheConfig),
|
|
79
|
+
defaultOptions: {
|
|
80
|
+
watchQuery: {
|
|
81
|
+
fetchPolicy: "cache-and-network",
|
|
82
|
+
errorPolicy: "all",
|
|
83
|
+
returnPartialData: false
|
|
84
|
+
},
|
|
85
|
+
query: { fetchPolicy: "network-only", errorPolicy: "all" },
|
|
86
|
+
mutate: { errorPolicy: "none" }
|
|
87
|
+
},
|
|
88
|
+
...config.apolloOptions
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/server/cmsServerClient.ts
|
|
93
|
+
var reactCache = React__namespace.cache ?? ((fn) => fn);
|
|
94
|
+
function createCmsServerClient(config) {
|
|
95
|
+
const resolved = resolveConfig({
|
|
96
|
+
cmsUrl: config.cmsUrl,
|
|
97
|
+
apiKey: config.apiKey,
|
|
98
|
+
graphqlPath: config.graphqlPath,
|
|
99
|
+
headers: config.headers
|
|
100
|
+
});
|
|
101
|
+
const next = config.revalidate !== void 0 || config.tags !== void 0 ? { revalidate: config.revalidate, tags: config.tags } : void 0;
|
|
102
|
+
const factory = config.getClient ?? (() => createApolloClient({
|
|
103
|
+
cmsUrl: resolved.cmsUrl,
|
|
104
|
+
apiKey: resolved.apiKey,
|
|
105
|
+
graphqlPath: resolved.graphqlPath,
|
|
106
|
+
headers: resolved.headers,
|
|
107
|
+
...next ? { apolloOptions: { defaultContext: { fetchOptions: { next } } } } : {}
|
|
108
|
+
}));
|
|
109
|
+
return { getClient: reactCache(factory), cmsUrl: resolved.cmsUrl };
|
|
110
|
+
}
|
|
111
|
+
function buildSelection(items = []) {
|
|
112
|
+
const lines = [];
|
|
113
|
+
items.forEach((item) => {
|
|
114
|
+
if (typeof item === "string") {
|
|
115
|
+
lines.push(`${item}: dataField(slug: "${item}")`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if ("field" in item && typeof item.field === "string") {
|
|
119
|
+
if (!("select" in item)) {
|
|
120
|
+
const alias2 = item.as || item.field;
|
|
121
|
+
lines.push(`${alias2}: dataField(slug: "${item.field}")`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const alias = item.as || item.field;
|
|
125
|
+
const resolver = item.single ? "expandedReferenceObject" : "expandedReference";
|
|
126
|
+
const subSelection = item.select?.length ? buildSelection(item.select) : "data { ... }";
|
|
127
|
+
lines.push(`
|
|
128
|
+
${alias}: ${resolver}(slug: "${item.field}") {
|
|
129
|
+
${subSelection}
|
|
130
|
+
}
|
|
131
|
+
`);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
return lines.join("\n ").trim();
|
|
135
|
+
}
|
|
136
|
+
function buildCmsQuery({
|
|
137
|
+
schema_slug,
|
|
138
|
+
entrySlug,
|
|
139
|
+
select = [],
|
|
140
|
+
fullData = false,
|
|
141
|
+
limit,
|
|
142
|
+
offset,
|
|
143
|
+
status = "PUBLISHED",
|
|
144
|
+
filter,
|
|
145
|
+
search,
|
|
146
|
+
variables = {}
|
|
147
|
+
}) {
|
|
148
|
+
const isSingle = Boolean(entrySlug);
|
|
149
|
+
const selectionParts = [];
|
|
150
|
+
if (fullData) {
|
|
151
|
+
selectionParts.push("data");
|
|
152
|
+
}
|
|
153
|
+
if (select.length > 0) {
|
|
154
|
+
const userSelection = buildSelection(select);
|
|
155
|
+
if (userSelection) {
|
|
156
|
+
selectionParts.push(userSelection);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const selection = selectionParts.join("\n ").trim() || "id";
|
|
160
|
+
const queryVariables = {
|
|
161
|
+
schema_slug,
|
|
162
|
+
...entrySlug && { slug: entrySlug },
|
|
163
|
+
...variables
|
|
164
|
+
};
|
|
165
|
+
const fieldArgParts = ["schema_slug: $schema_slug"];
|
|
166
|
+
if (!isSingle) {
|
|
167
|
+
if (typeof limit === "number" && limit >= 0) {
|
|
168
|
+
fieldArgParts.push("limit: $limit");
|
|
169
|
+
queryVariables.limit = limit;
|
|
170
|
+
}
|
|
171
|
+
if (typeof offset === "number" && offset >= 0) {
|
|
172
|
+
fieldArgParts.push("offset: $offset");
|
|
173
|
+
queryVariables.offset = offset;
|
|
174
|
+
}
|
|
175
|
+
if (status) {
|
|
176
|
+
const statuses = status;
|
|
177
|
+
if (statuses.length > 0) {
|
|
178
|
+
fieldArgParts.push("status: $status");
|
|
179
|
+
queryVariables.status = statuses;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const hasSearch = Array.isArray(search) && search.length > 0;
|
|
183
|
+
const hasFilter = filter && typeof filter === "object" && Object.keys(filter).length > 0;
|
|
184
|
+
if (hasSearch || hasFilter) {
|
|
185
|
+
const mergedFilter = hasFilter ? { ...filter } : {};
|
|
186
|
+
if (hasSearch) {
|
|
187
|
+
for (const condition of search) {
|
|
188
|
+
mergedFilter[condition.field] = {
|
|
189
|
+
regex: true,
|
|
190
|
+
value: condition.value,
|
|
191
|
+
mode: condition.mode ?? "i"
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
fieldArgParts.push("data: $filter");
|
|
196
|
+
queryVariables.filter = mergedFilter;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const varDecls = ["$schema_slug: String!"];
|
|
200
|
+
if ("limit" in queryVariables) varDecls.push("$limit: Float");
|
|
201
|
+
if ("offset" in queryVariables) varDecls.push("$offset: Float");
|
|
202
|
+
if ("status" in queryVariables) varDecls.push("$status: ContentStatus");
|
|
203
|
+
if ("filter" in queryVariables) varDecls.push("$filter: JSONObject");
|
|
204
|
+
const varBlock = varDecls.length > 0 ? `(
|
|
205
|
+
${varDecls.join("\n ")}
|
|
206
|
+
)` : "";
|
|
207
|
+
const contentFieldArgs = fieldArgParts.join(", ");
|
|
208
|
+
const queryStr = isSingle ? `
|
|
209
|
+
query Get${schema_slug}Entry($schema_slug: String!, $slug: String!) {
|
|
210
|
+
entry: contentEntry(schema_slug: $schema_slug, slug: $slug) {
|
|
211
|
+
${selection}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
` : `
|
|
215
|
+
query Get${schema_slug}Entries${varBlock} {
|
|
216
|
+
entries: contentEntries(${contentFieldArgs}) {
|
|
217
|
+
${selection}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
`;
|
|
221
|
+
return {
|
|
222
|
+
query: client$1.gql(queryStr),
|
|
223
|
+
variables: queryVariables,
|
|
224
|
+
isSingle
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/fetchCmsContent.ts
|
|
229
|
+
async function fetchCmsContent(getClient, opts) {
|
|
230
|
+
const { query, variables, isSingle } = buildCmsQuery(opts);
|
|
231
|
+
const { data } = await getClient().query({ query, variables });
|
|
232
|
+
return isSingle ? data.entry : data.entries;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/server/defineArticleSource.ts
|
|
236
|
+
function buildSearchConditions(fields, query) {
|
|
237
|
+
const trimmed = query?.trim();
|
|
238
|
+
if (!trimmed) return void 0;
|
|
239
|
+
return fields.map((field) => ({ field, value: trimmed, mode: "i" }));
|
|
240
|
+
}
|
|
241
|
+
function defineArticleSource(config) {
|
|
242
|
+
return Object.freeze({
|
|
243
|
+
client: config.client,
|
|
244
|
+
schemaSlug: config.schemaSlug,
|
|
245
|
+
listSelect: config.listSelect,
|
|
246
|
+
detailSelect: config.detailSelect,
|
|
247
|
+
seo: config.seo.cmsUrl ? config.seo : { ...config.seo, cmsUrl: config.client.cmsUrl },
|
|
248
|
+
searchFields: config.searchFields ?? ["title", "description"],
|
|
249
|
+
status: config.status ?? "PUBLISHED",
|
|
250
|
+
articleType: config.articleType ?? "Article",
|
|
251
|
+
relatedLimit: config.relatedLimit ?? 3,
|
|
252
|
+
groupPostsByCategory: config.groupPostsByCategory
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
async function fetchArticles(source, opts = {}) {
|
|
256
|
+
const search = buildSearchConditions(source.searchFields, opts.searchQuery);
|
|
257
|
+
const data = await fetchCmsContent(source.client.getClient, {
|
|
258
|
+
schema_slug: source.schemaSlug,
|
|
259
|
+
select: source.listSelect,
|
|
260
|
+
status: source.status,
|
|
261
|
+
...opts.limit !== void 0 ? { limit: opts.limit } : {},
|
|
262
|
+
...opts.categorySlug ? { filter: { category: opts.categorySlug } } : {},
|
|
263
|
+
...search ? { search } : {}
|
|
264
|
+
});
|
|
265
|
+
return Array.isArray(data) ? data : [];
|
|
266
|
+
}
|
|
267
|
+
async function fetchArticle(source, slug) {
|
|
268
|
+
try {
|
|
269
|
+
const data = await fetchCmsContent(source.client.getClient, {
|
|
270
|
+
schema_slug: source.schemaSlug,
|
|
271
|
+
entrySlug: slug,
|
|
272
|
+
select: source.detailSelect,
|
|
273
|
+
status: source.status
|
|
274
|
+
});
|
|
275
|
+
return data ?? null;
|
|
276
|
+
} catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function fetchRelatedArticles(source, post, slug) {
|
|
281
|
+
const categorySlug = post.category?.slug?.trim();
|
|
282
|
+
if (!categorySlug || source.relatedLimit <= 0) return [];
|
|
283
|
+
const data = await fetchCmsContent(source.client.getClient, {
|
|
284
|
+
schema_slug: source.schemaSlug,
|
|
285
|
+
select: source.listSelect,
|
|
286
|
+
status: source.status,
|
|
287
|
+
limit: source.relatedLimit + 1,
|
|
288
|
+
filter: { category: categorySlug }
|
|
289
|
+
});
|
|
290
|
+
return (Array.isArray(data) ? data : []).filter((p) => p.slug !== slug).slice(0, source.relatedLimit);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/seo/jsonld.ts
|
|
294
|
+
function buildArticleJsonLd(props) {
|
|
295
|
+
return {
|
|
296
|
+
"@context": "https://schema.org",
|
|
297
|
+
"@type": props.articleType ?? "Article",
|
|
298
|
+
headline: props.title,
|
|
299
|
+
description: props.description,
|
|
300
|
+
url: props.url,
|
|
301
|
+
...props.image ? { image: props.image } : {},
|
|
302
|
+
...props.publishedTime ? { datePublished: props.publishedTime } : {},
|
|
303
|
+
...props.category ? { articleSection: props.category } : {},
|
|
304
|
+
...props.tags && props.tags.length > 0 ? { keywords: props.tags.join(", ") } : {},
|
|
305
|
+
author: { "@type": "Person", name: props.authorName || props.siteName },
|
|
306
|
+
publisher: { "@id": `${props.siteUrl}/#organization` },
|
|
307
|
+
isPartOf: { "@id": `${props.siteUrl}/#website` },
|
|
308
|
+
inLanguage: "en-US"
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function buildCollectionJsonLd(props) {
|
|
312
|
+
return {
|
|
313
|
+
"@context": "https://schema.org",
|
|
314
|
+
"@type": "CollectionPage",
|
|
315
|
+
name: props.name,
|
|
316
|
+
description: props.description,
|
|
317
|
+
url: props.url,
|
|
318
|
+
isPartOf: { "@id": `${props.siteUrl}/#website` },
|
|
319
|
+
publisher: { "@id": `${props.siteUrl}/#organization` },
|
|
320
|
+
inLanguage: "en-US"
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/utils/cmsImage.ts
|
|
325
|
+
function cmsImage(id, options) {
|
|
326
|
+
if (!id) return "";
|
|
327
|
+
const base = options.cmsUrl.replace(/\/+$/, "");
|
|
328
|
+
const path = (options.mediaPath ?? "/media/canonical").replace(/^\/?/, "/");
|
|
329
|
+
return `${base}${path}/${id}`;
|
|
330
|
+
}
|
|
331
|
+
function createImageResolver(options) {
|
|
332
|
+
return (idOrUrl) => {
|
|
333
|
+
if (!idOrUrl) return "";
|
|
334
|
+
if (/^https?:\/\//i.test(idOrUrl)) return idOrUrl;
|
|
335
|
+
if (!options.cmsUrl) return "";
|
|
336
|
+
return cmsImage(idOrUrl, { cmsUrl: options.cmsUrl, mediaPath: options.mediaPath });
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// src/components/articles/articles.state.ts
|
|
341
|
+
function defaultGetCategoryName(post) {
|
|
342
|
+
return post.category?.name?.trim() || void 0;
|
|
343
|
+
}
|
|
344
|
+
function defaultGroupPostsByCategory(posts) {
|
|
345
|
+
const groups = /* @__PURE__ */ new Map();
|
|
346
|
+
for (const post of posts) {
|
|
347
|
+
const categoryName = defaultGetCategoryName(post) || "Other";
|
|
348
|
+
const categorySlug = post.category?.slug || "other";
|
|
349
|
+
const existing = groups.get(categorySlug);
|
|
350
|
+
if (existing) {
|
|
351
|
+
existing.posts.push(post);
|
|
352
|
+
} else {
|
|
353
|
+
groups.set(categorySlug, { categoryName, categorySlug, posts: [post] });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return Array.from(groups.values());
|
|
357
|
+
}
|
|
358
|
+
function applyPostFilters(posts, { categorySlug, articleSlug }) {
|
|
359
|
+
let filtered = posts;
|
|
360
|
+
if (categorySlug) {
|
|
361
|
+
filtered = filtered.filter((post) => post.category?.slug === categorySlug);
|
|
362
|
+
}
|
|
363
|
+
if (articleSlug) {
|
|
364
|
+
filtered = filtered.filter((post) => post.slug === articleSlug);
|
|
365
|
+
}
|
|
366
|
+
return filtered;
|
|
367
|
+
}
|
|
368
|
+
function splitFeaturedAndRest(posts) {
|
|
369
|
+
const featured = posts.find((post) => post.featured) ?? null;
|
|
370
|
+
const rest = featured ? posts.filter((post) => post.slug !== featured.slug) : [...posts];
|
|
371
|
+
return { featured, rest };
|
|
372
|
+
}
|
|
373
|
+
function buildArticlesViewState(rawPosts, options) {
|
|
374
|
+
const {
|
|
375
|
+
categorySlug,
|
|
376
|
+
articleSlug,
|
|
377
|
+
searchQuery = "",
|
|
378
|
+
groupPostsByCategory = defaultGroupPostsByCategory
|
|
379
|
+
} = options;
|
|
380
|
+
const posts = applyPostFilters(rawPosts, { categorySlug, articleSlug });
|
|
381
|
+
const { featured, rest } = splitFeaturedAndRest(posts);
|
|
382
|
+
const trimmedQuery = searchQuery.trim();
|
|
383
|
+
const isSearching = trimmedQuery.length > 0;
|
|
384
|
+
const categoryGroups = isSearching ? posts.length === 0 ? [] : [
|
|
385
|
+
{
|
|
386
|
+
categoryName: `Search results for "${trimmedQuery}"`,
|
|
387
|
+
categorySlug: "search-results",
|
|
388
|
+
posts
|
|
389
|
+
}
|
|
390
|
+
] : groupPostsByCategory(rest);
|
|
391
|
+
return {
|
|
392
|
+
posts,
|
|
393
|
+
featured,
|
|
394
|
+
rest,
|
|
395
|
+
categoryGroups,
|
|
396
|
+
isEmpty: !featured && rest.length === 0,
|
|
397
|
+
isSearching,
|
|
398
|
+
searchQuery: trimmedQuery
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function renderArticlesListingBody(args) {
|
|
402
|
+
const { state, hasError, error, cmsImage: cmsImage2, searchNode, seoNode, jsonLdNode, renderProps } = args;
|
|
403
|
+
const {
|
|
404
|
+
eyebrow,
|
|
405
|
+
title,
|
|
406
|
+
description,
|
|
407
|
+
renderRoot,
|
|
408
|
+
renderHeader,
|
|
409
|
+
renderFeaturedCard,
|
|
410
|
+
renderPostCard,
|
|
411
|
+
renderCategoryHeading,
|
|
412
|
+
renderPostGrid,
|
|
413
|
+
renderCategoryGroup,
|
|
414
|
+
renderSkeleton,
|
|
415
|
+
renderEmpty,
|
|
416
|
+
renderContent
|
|
417
|
+
} = renderProps;
|
|
418
|
+
const headerNode = renderHeader ? renderHeader({ eyebrow, title, description, search: searchNode }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
419
|
+
eyebrow,
|
|
420
|
+
title,
|
|
421
|
+
description,
|
|
422
|
+
searchNode
|
|
423
|
+
] });
|
|
424
|
+
const featuredNode = state.featured && !state.isSearching && renderFeaturedCard ? renderFeaturedCard({ post: state.featured, cmsImage: cmsImage2 }) : null;
|
|
425
|
+
const noSearchResultsNode = state.isSearching && true && state.posts.length === 0 ? renderEmpty?.({ reason: "no-results", searchQuery: state.searchQuery }) ?? null : null;
|
|
426
|
+
const groupsNode = noSearchResultsNode ? null : state.categoryGroups.map((group) => {
|
|
427
|
+
const postCards = group.posts.map((post, index) => /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: renderPostCard({ post, index, group, cmsImage: cmsImage2 }) }, post.slug ?? index));
|
|
428
|
+
const gridNode = renderPostGrid ? renderPostGrid({ posts: group.posts, group, children: postCards }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: postCards });
|
|
429
|
+
const headingNode = renderCategoryHeading ? renderCategoryHeading({ group }) : group.categoryName;
|
|
430
|
+
const defaultContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
431
|
+
headingNode,
|
|
432
|
+
gridNode
|
|
433
|
+
] });
|
|
434
|
+
return /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: renderCategoryGroup ? renderCategoryGroup({ group, defaultContent }) : defaultContent }, group.categorySlug);
|
|
435
|
+
});
|
|
436
|
+
const contentNode = !hasError && (state.featured || state.rest.length > 0) ? renderContent ? renderContent({ featured: featuredNode, groups: groupsNode, noSearchResults: noSearchResultsNode }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
437
|
+
featuredNode,
|
|
438
|
+
noSearchResultsNode,
|
|
439
|
+
groupsNode
|
|
440
|
+
] }) : null;
|
|
441
|
+
const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
442
|
+
seoNode,
|
|
443
|
+
jsonLdNode,
|
|
444
|
+
headerNode,
|
|
445
|
+
null,
|
|
446
|
+
(hasError || state.isEmpty) ? renderEmpty?.({
|
|
447
|
+
reason: hasError ? "error" : state.isSearching ? "no-results" : "no-posts",
|
|
448
|
+
searchQuery: state.searchQuery,
|
|
449
|
+
error
|
|
450
|
+
}) : null,
|
|
451
|
+
contentNode
|
|
452
|
+
] });
|
|
453
|
+
return renderRoot ? renderRoot({ children: body }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: body });
|
|
454
|
+
}
|
|
455
|
+
async function AsteroidArticlesListingServer(props) {
|
|
456
|
+
const {
|
|
457
|
+
source,
|
|
458
|
+
searchQuery = "",
|
|
459
|
+
categorySlug,
|
|
460
|
+
articleSlug,
|
|
461
|
+
searchParamKey = "q",
|
|
462
|
+
limit,
|
|
463
|
+
noindex,
|
|
464
|
+
searchBoxProps,
|
|
465
|
+
renderSearch,
|
|
466
|
+
renderJsonLd,
|
|
467
|
+
groupPostsByCategory,
|
|
468
|
+
...renderProps
|
|
469
|
+
} = props;
|
|
470
|
+
let rawPosts = [];
|
|
471
|
+
let hasError = false;
|
|
472
|
+
let error = void 0;
|
|
473
|
+
try {
|
|
474
|
+
rawPosts = await fetchArticles(source, { searchQuery, categorySlug, limit });
|
|
475
|
+
} catch (err) {
|
|
476
|
+
hasError = true;
|
|
477
|
+
error = err;
|
|
478
|
+
}
|
|
479
|
+
const state = buildArticlesViewState(rawPosts, {
|
|
480
|
+
categorySlug,
|
|
481
|
+
articleSlug,
|
|
482
|
+
searchQuery,
|
|
483
|
+
groupPostsByCategory
|
|
484
|
+
});
|
|
485
|
+
const cmsImage2 = createImageResolver({ cmsUrl: source.seo.cmsUrl ?? source.client.cmsUrl });
|
|
486
|
+
const categoryName = categorySlug ? state.posts[0]?.category?.name?.trim() : void 0;
|
|
487
|
+
const collection = buildCollectionJsonLd({
|
|
488
|
+
name: categoryName || `${source.seo.siteName} ${source.seo.contentLabel ?? "Articles"}`,
|
|
489
|
+
description: source.seo.defaultDescription || "",
|
|
490
|
+
url: `${(source.seo.baseUrl || "").replace(/\/$/, "")}${source.seo.articlePath ?? "/blog"}${categorySlug ? `/category/${categorySlug}` : ""}`,
|
|
491
|
+
siteUrl: (source.seo.baseUrl || "").replace(/\/$/, "")
|
|
492
|
+
});
|
|
493
|
+
const jsonLdNode = renderJsonLd?.(state) ?? /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(collection) } });
|
|
494
|
+
const searchNode = renderSearch ? renderSearch({ value: searchQuery, onChange: () => {
|
|
495
|
+
}, onSubmit: (e) => e.preventDefault() }) : /* @__PURE__ */ jsxRuntime.jsx(client.ArticleSearchBox, { paramKey: searchParamKey, ...searchBoxProps });
|
|
496
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderArticlesListingBody({
|
|
497
|
+
state,
|
|
498
|
+
hasError,
|
|
499
|
+
error,
|
|
500
|
+
cmsImage: cmsImage2,
|
|
501
|
+
searchNode,
|
|
502
|
+
seoNode: null,
|
|
503
|
+
jsonLdNode,
|
|
504
|
+
renderProps
|
|
505
|
+
}) });
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/seo/seo.builders.ts
|
|
509
|
+
function applyTitleTemplate(config, title) {
|
|
510
|
+
return config.titleTemplate ? config.titleTemplate(title) : `${title} | ${config.siteName}`;
|
|
511
|
+
}
|
|
512
|
+
function buildOgImageUrl(config, params) {
|
|
513
|
+
if (config.getOgImageUrl) {
|
|
514
|
+
return config.getOgImageUrl(params);
|
|
515
|
+
}
|
|
516
|
+
const palette = config.ogImage?.palette;
|
|
517
|
+
if (!palette) return void 0;
|
|
518
|
+
const apiPath = config.ogImage?.apiPath ?? "/api/og";
|
|
519
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
520
|
+
const searchParams = new URLSearchParams({
|
|
521
|
+
title: params.title,
|
|
522
|
+
type: params.type ?? "article",
|
|
523
|
+
siteName: config.siteName,
|
|
524
|
+
bg: palette.background,
|
|
525
|
+
fg: palette.foreground,
|
|
526
|
+
accent: palette.accent
|
|
527
|
+
});
|
|
528
|
+
if (params.subtitle?.trim()) searchParams.set("subtitle", params.subtitle.trim());
|
|
529
|
+
if (params.eyebrow?.trim()) searchParams.set("eyebrow", params.eyebrow.trim());
|
|
530
|
+
if (palette.accentMuted) searchParams.set("accentMuted", palette.accentMuted);
|
|
531
|
+
if (palette.mutedText) searchParams.set("muted", palette.mutedText);
|
|
532
|
+
return `${base}${apiPath}?${searchParams.toString()}`;
|
|
533
|
+
}
|
|
534
|
+
function resolveArticleImage(post, config) {
|
|
535
|
+
const featuredImage = config.cmsUrl ? cmsImage(post.featured_image, { cmsUrl: config.cmsUrl }) : "";
|
|
536
|
+
if (featuredImage) return featuredImage;
|
|
537
|
+
const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription;
|
|
538
|
+
return buildOgImageUrl(config, {
|
|
539
|
+
title: post.title,
|
|
540
|
+
subtitle: description,
|
|
541
|
+
eyebrow: config.contentLabel ?? "Article",
|
|
542
|
+
type: "article"
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
function buildArticleSeoValues(post, config, slug, options) {
|
|
546
|
+
const articlePath = config.articlePath ?? "/blog";
|
|
547
|
+
const url = `${config.baseUrl.replace(/\/$/, "")}${articlePath}/${slug}`;
|
|
548
|
+
const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription || `Read the latest from ${config.siteName}.`;
|
|
549
|
+
return {
|
|
550
|
+
title: applyTitleTemplate(config, post.title),
|
|
551
|
+
siteName: config.siteName,
|
|
552
|
+
twitter: config.twitter ?? "",
|
|
553
|
+
description,
|
|
554
|
+
url,
|
|
555
|
+
keywords: config.defaultKeywords ?? post.title,
|
|
556
|
+
image: resolveArticleImage(post, config),
|
|
557
|
+
noindex: options?.noindex ?? config.noindex,
|
|
558
|
+
manifestUrl: config.manifestUrl
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function buildArticleListingSeoValues(config, options) {
|
|
562
|
+
const articlePath = config.articlePath ?? "/blog";
|
|
563
|
+
const base = config.baseUrl.replace(/\/$/, "");
|
|
564
|
+
const label = config.contentLabel ?? "Articles";
|
|
565
|
+
const categoryName = options?.categoryName?.trim();
|
|
566
|
+
const categorySlug = options?.categorySlug?.trim();
|
|
567
|
+
const titleText = categoryName ? `${categoryName} ${label}` : label;
|
|
568
|
+
const description = categoryName ? `Explore ${categoryName} ${label.toLowerCase()}, guides, and the latest updates from ${config.siteName}.` : config.defaultDescription || `Browse ${label.toLowerCase()}, insights, and the latest updates from ${config.siteName}.`;
|
|
569
|
+
const url = categorySlug ? `${base}${articlePath}/category/${categorySlug}` : `${base}${articlePath}`;
|
|
570
|
+
return {
|
|
571
|
+
title: applyTitleTemplate(config, titleText),
|
|
572
|
+
siteName: config.siteName,
|
|
573
|
+
twitter: config.twitter ?? "",
|
|
574
|
+
description,
|
|
575
|
+
url,
|
|
576
|
+
keywords: config.defaultKeywords ?? (categoryName ? `${categoryName}, ${config.siteName}` : `${config.siteName} ${label.toLowerCase()}`),
|
|
577
|
+
image: buildOgImageUrl(config, {
|
|
578
|
+
title: titleText,
|
|
579
|
+
subtitle: description,
|
|
580
|
+
eyebrow: categoryName ? "Category" : label,
|
|
581
|
+
type: "listing"
|
|
582
|
+
}),
|
|
583
|
+
noindex: options?.noindex ?? config.noindex,
|
|
584
|
+
manifestUrl: config.manifestUrl
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function renderArticleBody(args) {
|
|
588
|
+
const { post, cmsImage: cmsImage2, relatedPosts, seoNode, jsonLdNode, renderProps: r } = args;
|
|
589
|
+
const slot = { post, cmsImage: cmsImage2 };
|
|
590
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
591
|
+
seoNode,
|
|
592
|
+
jsonLdNode,
|
|
593
|
+
r.backLink,
|
|
594
|
+
r.renderPreArticle?.(slot),
|
|
595
|
+
r.renderHeader?.(slot),
|
|
596
|
+
r.renderMeta?.(slot),
|
|
597
|
+
r.renderDescription?.(slot),
|
|
598
|
+
r.renderFeaturedImage?.(slot),
|
|
599
|
+
r.renderToc?.(slot),
|
|
600
|
+
r.renderContent?.(slot),
|
|
601
|
+
r.renderMidArticle?.(slot),
|
|
602
|
+
r.renderTags?.(slot),
|
|
603
|
+
r.renderAuthorDetails?.(slot),
|
|
604
|
+
r.renderRelatedPosts?.({ post, relatedPosts, cmsImage: cmsImage2 }),
|
|
605
|
+
r.renderCTA?.(slot),
|
|
606
|
+
r.renderPostArticle?.(slot)
|
|
607
|
+
] });
|
|
608
|
+
}
|
|
609
|
+
async function AsteroidArticlePageServer(props) {
|
|
610
|
+
const { source, slug, articleType, noindex, renderError, renderJsonLd, ...bodyRenderProps } = props;
|
|
611
|
+
const article = await fetchArticle(source, slug);
|
|
612
|
+
if (!article) {
|
|
613
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderError?.({ reason: "not-found" }) ?? null });
|
|
614
|
+
}
|
|
615
|
+
const relatedPosts = await fetchRelatedArticles(source, article, slug);
|
|
616
|
+
const cmsImage2 = createImageResolver({ cmsUrl: source.seo.cmsUrl ?? source.client.cmsUrl });
|
|
617
|
+
const type = articleType ?? source.articleType;
|
|
618
|
+
const seoValues = buildArticleSeoValues(article, source.seo, slug, { noindex });
|
|
619
|
+
const articleLd = buildArticleJsonLd({
|
|
620
|
+
title: article.title,
|
|
621
|
+
description: article.description || source.seo.defaultDescription || "",
|
|
622
|
+
url: `${(source.seo.baseUrl || "").replace(/\/$/, "")}${source.seo.articlePath ?? "/blog"}/${slug}`,
|
|
623
|
+
siteName: source.seo.siteName,
|
|
624
|
+
siteUrl: (source.seo.baseUrl || "").replace(/\/$/, ""),
|
|
625
|
+
articleType: type,
|
|
626
|
+
image: seoValues.image,
|
|
627
|
+
authorName: article.author?.name,
|
|
628
|
+
publishedTime: article.published_date || void 0,
|
|
629
|
+
tags: article.tags?.split(",").map((t) => t.trim()).filter(Boolean),
|
|
630
|
+
category: article.category?.name
|
|
631
|
+
});
|
|
632
|
+
const jsonLdNode = renderJsonLd?.({ post: article }) ?? /* @__PURE__ */ jsxRuntime.jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(articleLd) } });
|
|
633
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderArticleBody({
|
|
634
|
+
post: article,
|
|
635
|
+
cmsImage: cmsImage2,
|
|
636
|
+
relatedPosts,
|
|
637
|
+
seoNode: null,
|
|
638
|
+
jsonLdNode,
|
|
639
|
+
renderProps: bodyRenderProps
|
|
640
|
+
}) });
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/server/articleMetadata.ts
|
|
644
|
+
function seoValuesToMetadata(v, ogType) {
|
|
645
|
+
return {
|
|
646
|
+
title: v.title,
|
|
647
|
+
description: v.description,
|
|
648
|
+
publisher: v.siteName,
|
|
649
|
+
keywords: v.keywords,
|
|
650
|
+
category: v.title,
|
|
651
|
+
...v.manifestUrl ? { manifest: v.manifestUrl } : {},
|
|
652
|
+
robots: v.noindex ? { index: false, follow: true } : { index: true, follow: true },
|
|
653
|
+
authors: { name: v.siteName },
|
|
654
|
+
referrer: "origin",
|
|
655
|
+
abstract: v.description,
|
|
656
|
+
alternates: { canonical: v.url },
|
|
657
|
+
openGraph: {
|
|
658
|
+
title: v.title,
|
|
659
|
+
description: v.description,
|
|
660
|
+
url: v.url,
|
|
661
|
+
siteName: v.siteName,
|
|
662
|
+
locale: "en_US",
|
|
663
|
+
type: ogType,
|
|
664
|
+
...v.image ? { images: [{ url: v.image }] } : {}
|
|
665
|
+
},
|
|
666
|
+
twitter: {
|
|
667
|
+
title: v.title,
|
|
668
|
+
description: v.description,
|
|
669
|
+
site: v.twitter || void 0,
|
|
670
|
+
card: v.image ? "summary_large_image" : "summary",
|
|
671
|
+
...v.image ? { images: [v.image] } : {}
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
async function generateListingMetadata(source, options) {
|
|
676
|
+
return seoValuesToMetadata(buildArticleListingSeoValues(source.seo, options), "website");
|
|
677
|
+
}
|
|
678
|
+
async function generateArticleMetadata(source, paramsOrSlug) {
|
|
679
|
+
const resolved = await paramsOrSlug;
|
|
680
|
+
const slug = typeof resolved === "string" ? resolved : resolved.slug;
|
|
681
|
+
const article = await fetchArticle(source, slug);
|
|
682
|
+
if (!article) {
|
|
683
|
+
return seoValuesToMetadata(buildArticleListingSeoValues(source.seo), "website");
|
|
684
|
+
}
|
|
685
|
+
return seoValuesToMetadata(buildArticleSeoValues(article, source.seo, slug), "article");
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
exports.AsteroidArticlePageServer = AsteroidArticlePageServer;
|
|
689
|
+
exports.AsteroidArticlesListingServer = AsteroidArticlesListingServer;
|
|
690
|
+
exports.buildSearchConditions = buildSearchConditions;
|
|
691
|
+
exports.createCmsServerClient = createCmsServerClient;
|
|
692
|
+
exports.defineArticleSource = defineArticleSource;
|
|
693
|
+
exports.fetchArticle = fetchArticle;
|
|
694
|
+
exports.fetchArticles = fetchArticles;
|
|
695
|
+
exports.fetchRelatedArticles = fetchRelatedArticles;
|
|
696
|
+
exports.generateArticleMetadata = generateArticleMetadata;
|
|
697
|
+
exports.generateListingMetadata = generateListingMetadata;
|
|
698
|
+
//# sourceMappingURL=server.cjs.map
|
|
699
|
+
//# sourceMappingURL=server.cjs.map
|