@fullstackdatasolutions/articles 1.2.3 → 1.3.1
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/CHANGELOG.md +46 -0
- package/README.md +313 -1
- package/dist/index.cjs +308 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +267 -16
- package/dist/index.d.ts +267 -16
- package/dist/index.js +300 -79
- package/dist/index.js.map +1 -1
- package/dist/nextjs.cjs +325 -31
- package/dist/nextjs.cjs.map +1 -1
- package/dist/nextjs.d.cts +179 -2
- package/dist/nextjs.d.ts +179 -2
- package/dist/nextjs.js +325 -31
- package/dist/nextjs.js.map +1 -1
- package/dist/server.cjs +660 -50
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +333 -12
- package/dist/server.d.ts +333 -12
- package/dist/server.js +645 -50
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/ArticleAnswer.tsx +35 -0
- package/src/ArticleSchemas.tsx +263 -23
- package/src/AuthorArticlesPage.tsx +38 -8
- package/src/__tests__/ArticleAnswer.test.tsx +25 -0
- package/src/__tests__/ArticleSchemas.test.tsx +516 -0
- package/src/__tests__/AuthorArticlesPage.test.tsx +76 -0
- package/src/__tests__/authorUtils.test.ts +50 -0
- package/src/__tests__/markdown.test.ts +77 -1
- package/src/__tests__/nextjs.test.ts +31 -15
- package/src/__tests__/seoUtils.test.ts +279 -0
- package/src/__tests__/server-articles.test.ts +434 -1
- package/src/__tests__/validateArticles.test.ts +167 -6
- package/src/articleTypes.ts +57 -0
- package/src/articlesConfig.ts +176 -1
- package/src/authorUtils.ts +19 -1
- package/src/errorReporting.ts +1 -0
- package/src/index.ts +17 -1
- package/src/markdown.ts +100 -1
- package/src/nextjs.ts +7 -4
- package/src/seoUtils.ts +247 -26
- package/src/server-articles.ts +385 -25
- package/src/server.ts +35 -4
- package/src/validateArticles.ts +157 -12
package/dist/index.cjs
CHANGED
|
@@ -69,6 +69,7 @@ var __async = (__this, __arguments, generator) => {
|
|
|
69
69
|
// src/index.ts
|
|
70
70
|
var src_exports = {};
|
|
71
71
|
__export(src_exports, {
|
|
72
|
+
ArticleAnswer: () => ArticleAnswer,
|
|
72
73
|
ArticleBackLink: () => ArticleBackLink,
|
|
73
74
|
ArticleCard: () => ArticleCard,
|
|
74
75
|
ArticleCategoryGrid: () => ArticleCategoryGrid,
|
|
@@ -97,16 +98,23 @@ __export(src_exports, {
|
|
|
97
98
|
DEFAULT_CATEGORIES_PAGE_SIZE: () => DEFAULT_CATEGORIES_PAGE_SIZE,
|
|
98
99
|
DEFAULT_LAYOUT: () => DEFAULT_LAYOUT,
|
|
99
100
|
DEFAULT_PAGE_SIZE: () => DEFAULT_PAGE_SIZE,
|
|
101
|
+
DEFAULT_TITLE_TEMPLATE: () => DEFAULT_TITLE_TEMPLATE,
|
|
100
102
|
FAQPageSchema: () => FAQPageSchema,
|
|
101
103
|
FeaturedArticle: () => FeaturedArticle,
|
|
102
104
|
LatestArticles: () => LatestArticles,
|
|
103
105
|
LatestArticlesSection: () => LatestArticlesSection,
|
|
106
|
+
OrganizationSchema: () => OrganizationSchema,
|
|
104
107
|
PaginationNav: () => PaginationNav,
|
|
105
108
|
RelatedArticlesSection: () => RelatedArticlesSection,
|
|
106
109
|
ScrollToTop: () => ScrollToTop,
|
|
107
110
|
SeriesArticlesPage: () => SeriesArticlesPage,
|
|
111
|
+
WebSiteSchema: () => WebSiteSchema,
|
|
108
112
|
breadcrumbsAreEnabled: () => breadcrumbsAreEnabled,
|
|
113
|
+
formatPageTitle: () => formatPageTitle,
|
|
109
114
|
getBreadcrumbsConfig: () => getBreadcrumbsConfig,
|
|
115
|
+
getOrganizationId: () => getOrganizationId,
|
|
116
|
+
getPersonId: () => getPersonId,
|
|
117
|
+
getWebSiteId: () => getWebSiteId,
|
|
110
118
|
useArticles: () => useArticles
|
|
111
119
|
});
|
|
112
120
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -161,7 +169,51 @@ function ArticleCategoryGrid({ categories, pageSize = 8 }) {
|
|
|
161
169
|
] }) });
|
|
162
170
|
}
|
|
163
171
|
|
|
172
|
+
// src/articlesConfig.ts
|
|
173
|
+
var DEFAULT_PAGE_SIZE = 6;
|
|
174
|
+
var DEFAULT_CATEGORIES_PAGE_SIZE = 8;
|
|
175
|
+
var DEFAULT_LAYOUT = [
|
|
176
|
+
"hero",
|
|
177
|
+
"search",
|
|
178
|
+
"featured",
|
|
179
|
+
"latest",
|
|
180
|
+
"categories"
|
|
181
|
+
];
|
|
182
|
+
function breadcrumbsAreEnabled(config) {
|
|
183
|
+
var _a;
|
|
184
|
+
return config.breadcrumbs !== false && ((_a = config.breadcrumbs) == null ? void 0 : _a.show) !== false;
|
|
185
|
+
}
|
|
186
|
+
function getBreadcrumbsConfig(config) {
|
|
187
|
+
var _a;
|
|
188
|
+
if (config.breadcrumbs === false) return {};
|
|
189
|
+
return (_a = config.breadcrumbs) != null ? _a : {};
|
|
190
|
+
}
|
|
191
|
+
function getOrganizationId(config) {
|
|
192
|
+
return `${config.siteUrl.replace(/\/$/, "")}/#organization`;
|
|
193
|
+
}
|
|
194
|
+
function getWebSiteId(config) {
|
|
195
|
+
return `${config.siteUrl.replace(/\/$/, "")}/#website`;
|
|
196
|
+
}
|
|
197
|
+
function getPersonId(authorUrl) {
|
|
198
|
+
return `${authorUrl.replace(/\/$/, "")}#person`;
|
|
199
|
+
}
|
|
200
|
+
function resolveEntityUrl(value, config) {
|
|
201
|
+
if (/^https?:\/\//.test(value)) return value;
|
|
202
|
+
return `${config.siteUrl.replace(/\/$/, "")}/${value.replace(/^\/+/, "")}`;
|
|
203
|
+
}
|
|
204
|
+
var DEFAULT_TITLE_TEMPLATE = "{title} | {siteName}";
|
|
205
|
+
function formatPageTitle(title, config) {
|
|
206
|
+
var _a;
|
|
207
|
+
return ((_a = config.titleTemplate) != null ? _a : DEFAULT_TITLE_TEMPLATE).replaceAll("{title}", title).replaceAll("{siteName}", config.siteName);
|
|
208
|
+
}
|
|
209
|
+
|
|
164
210
|
// src/authorUtils.ts
|
|
211
|
+
function getAuthorIdentityUrl(author, config) {
|
|
212
|
+
if (author.identityUrl) return author.identityUrl;
|
|
213
|
+
if (author.url) return author.url;
|
|
214
|
+
if (!config) return void 0;
|
|
215
|
+
return `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`;
|
|
216
|
+
}
|
|
165
217
|
function getAuthorUrl(author) {
|
|
166
218
|
var _a;
|
|
167
219
|
return (_a = author.url) != null ? _a : `/articles/authors/${author.slug}`;
|
|
@@ -176,7 +228,10 @@ function getAuthorAvatar(author, config) {
|
|
|
176
228
|
return `${config.siteUrl.replace(/\/$/, "")}${path}`;
|
|
177
229
|
}
|
|
178
230
|
function getAuthorSameAs(author) {
|
|
179
|
-
|
|
231
|
+
var _a, _b;
|
|
232
|
+
const derived = getAuthorSocialLinks(author).map((link) => link.href);
|
|
233
|
+
const explicit = (_b = (_a = author.sameAs) == null ? void 0 : _a.filter((url) => url.trim() !== "")) != null ? _b : [];
|
|
234
|
+
return [.../* @__PURE__ */ new Set([...derived, ...explicit])];
|
|
180
235
|
}
|
|
181
236
|
function normalizeHandle(value) {
|
|
182
237
|
return value.replace(/^@/, "");
|
|
@@ -309,37 +364,129 @@ function getPersonSchemas(article, authors, config) {
|
|
|
309
364
|
} else if (article.author) {
|
|
310
365
|
resolvedAuthors = [{ name: article.author, slug: "", bio: "" }];
|
|
311
366
|
}
|
|
312
|
-
return resolvedAuthors.map((author) =>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
367
|
+
return resolvedAuthors.map((author) => {
|
|
368
|
+
const identityUrl = getAuthorIdentityUrl(author, config);
|
|
369
|
+
return __spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues({
|
|
370
|
+
"@type": "Person"
|
|
371
|
+
}, identityUrl && { "@id": getPersonId(identityUrl) }), {
|
|
372
|
+
name: author.name
|
|
373
|
+
}), author.url && { url: author.url }), getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) });
|
|
374
|
+
});
|
|
316
375
|
}
|
|
317
|
-
function
|
|
376
|
+
function resolveModifiedAt(article, publishedAt, config) {
|
|
377
|
+
if (article.lastmod) return new Date(article.lastmod).toISOString();
|
|
378
|
+
return (config == null ? void 0 : config.lastmodFallback) === "none" ? void 0 : publishedAt;
|
|
379
|
+
}
|
|
380
|
+
function resolveSchemaImageUrl(image, articleUrl, config) {
|
|
381
|
+
if (/^https?:\/\//.test(image)) return image;
|
|
382
|
+
if (config) return resolveEntityUrl(image, config);
|
|
383
|
+
try {
|
|
384
|
+
return `${new URL(articleUrl).origin}/${image.replace(/^\/+/, "")}`;
|
|
385
|
+
} catch (e) {
|
|
386
|
+
return image;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function buildPublisher(siteName, siteLogo, config) {
|
|
390
|
+
if (config == null ? void 0 : config.organization) return { "@id": getOrganizationId(config) };
|
|
391
|
+
return __spreadValues({
|
|
392
|
+
"@type": "Organization",
|
|
393
|
+
name: siteName
|
|
394
|
+
}, siteLogo && { logo: { "@type": "ImageObject", url: siteLogo } });
|
|
395
|
+
}
|
|
396
|
+
function buildQuestionEntity(article, articleUrl, publishedAt) {
|
|
397
|
+
if (article.articleType !== "QAPage" || !article.answer) return void 0;
|
|
398
|
+
return __spreadProps(__spreadValues({
|
|
399
|
+
"@type": "Question",
|
|
400
|
+
name: article.title,
|
|
401
|
+
text: article.excerpt || article.title
|
|
402
|
+
}, publishedAt && { dateCreated: publishedAt }), {
|
|
403
|
+
answerCount: 1,
|
|
404
|
+
acceptedAnswer: { "@type": "Answer", text: article.answer, url: articleUrl }
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
function buildCommentFields(comments) {
|
|
408
|
+
var _a;
|
|
409
|
+
const visible = (_a = comments == null ? void 0 : comments.filter((comment) => !comment.isDeleted && !comment.parentId)) != null ? _a : [];
|
|
410
|
+
if (visible.length === 0) return {};
|
|
411
|
+
return {
|
|
412
|
+
commentCount: visible.length,
|
|
413
|
+
comment: visible.map((comment) => ({
|
|
414
|
+
"@type": "Comment",
|
|
415
|
+
text: comment.body,
|
|
416
|
+
dateCreated: comment.createdAt,
|
|
417
|
+
author: { "@type": "Person", name: comment.authorName }
|
|
418
|
+
}))
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function buildTopicFields(article, config) {
|
|
422
|
+
var _a, _b;
|
|
423
|
+
const about = (_a = article.about) == null ? void 0 : _a.map((entity) => __spreadValues({
|
|
424
|
+
"@type": "Thing",
|
|
425
|
+
name: entity.name
|
|
426
|
+
}, entity.sameAs && { sameAs: entity.sameAs }));
|
|
427
|
+
const citation = (_b = article.citation) == null ? void 0 : _b.map((source) => __spreadValues({
|
|
428
|
+
"@type": "CreativeWork",
|
|
429
|
+
name: source.name
|
|
430
|
+
}, source.url && { url: source.url }));
|
|
431
|
+
const selectors = config == null ? void 0 : config.speakableSelectors;
|
|
432
|
+
return __spreadValues(__spreadValues(__spreadValues({}, (about == null ? void 0 : about.length) && { about }), (citation == null ? void 0 : citation.length) && { citation }), (selectors == null ? void 0 : selectors.length) && {
|
|
433
|
+
speakable: { "@type": "SpeakableSpecification", cssSelector: selectors }
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
function buildArticleSchema({
|
|
318
437
|
article,
|
|
319
438
|
articleUrl,
|
|
320
439
|
siteName,
|
|
321
440
|
siteLogo,
|
|
322
|
-
showAuthor
|
|
323
|
-
|
|
324
|
-
config
|
|
441
|
+
showAuthor,
|
|
442
|
+
personSchemas,
|
|
443
|
+
config,
|
|
444
|
+
comments
|
|
325
445
|
}) {
|
|
446
|
+
var _a, _b;
|
|
326
447
|
const publishedAt = article.date ? new Date(article.date).toISOString() : void 0;
|
|
327
|
-
const modifiedAt = article
|
|
328
|
-
const
|
|
329
|
-
|
|
448
|
+
const modifiedAt = resolveModifiedAt(article, publishedAt, config);
|
|
449
|
+
const mainEntity = buildQuestionEntity(article, articleUrl, publishedAt);
|
|
450
|
+
return __spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadValues({
|
|
330
451
|
"@context": "https://schema.org",
|
|
331
452
|
"@type": article.articleType || "Article",
|
|
332
453
|
mainEntityOfPage: { "@type": "WebPage", "@id": articleUrl },
|
|
333
454
|
headline: article.title,
|
|
334
|
-
image: article.featuredImage ? {
|
|
455
|
+
image: article.featuredImage ? {
|
|
456
|
+
"@type": "ImageObject",
|
|
457
|
+
url: resolveSchemaImageUrl(article.featuredImage, articleUrl, config)
|
|
458
|
+
} : void 0
|
|
335
459
|
}, publishedAt && { datePublished: publishedAt }), modifiedAt && { dateModified: modifiedAt }), showAuthor && personSchemas.length > 0 && { author: personSchemas }), {
|
|
336
|
-
publisher:
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}, siteLogo && { logo: { "@type": "ImageObject", url: siteLogo } }),
|
|
340
|
-
description: article.excerpt,
|
|
460
|
+
publisher: buildPublisher(siteName, siteLogo, config),
|
|
461
|
+
description: article.excerpt
|
|
462
|
+
}), article.answer && { abstract: article.answer }), {
|
|
341
463
|
articleSection: article.category
|
|
342
|
-
}),
|
|
464
|
+
}), ((_a = article.tags) == null ? void 0 : _a.length) && { keywords: article.tags.join(", ") }), article.wordCount !== void 0 && { wordCount: article.wordCount }), article.series && { isPartOf: { "@type": "Blog", name: article.series } }), {
|
|
465
|
+
inLanguage: (_b = config == null ? void 0 : config.language) != null ? _b : "en",
|
|
466
|
+
isAccessibleForFree: (config == null ? void 0 : config.isAccessibleForFree) !== false
|
|
467
|
+
}), buildTopicFields(article, config)), buildCommentFields(comments)), mainEntity && { mainEntity });
|
|
468
|
+
}
|
|
469
|
+
function ArticleSEO({
|
|
470
|
+
article,
|
|
471
|
+
articleUrl,
|
|
472
|
+
siteName,
|
|
473
|
+
siteLogo,
|
|
474
|
+
showAuthor = true,
|
|
475
|
+
authors,
|
|
476
|
+
config,
|
|
477
|
+
comments
|
|
478
|
+
}) {
|
|
479
|
+
const personSchemas = getPersonSchemas(article, authors, config);
|
|
480
|
+
const structuredData = buildArticleSchema({
|
|
481
|
+
article,
|
|
482
|
+
articleUrl,
|
|
483
|
+
siteName,
|
|
484
|
+
siteLogo,
|
|
485
|
+
showAuthor,
|
|
486
|
+
personSchemas,
|
|
487
|
+
config,
|
|
488
|
+
comments
|
|
489
|
+
});
|
|
343
490
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
344
491
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
345
492
|
"script",
|
|
@@ -436,6 +583,65 @@ function CollectionPageSchema({
|
|
|
436
583
|
}
|
|
437
584
|
);
|
|
438
585
|
}
|
|
586
|
+
function OrganizationSchema({ config }) {
|
|
587
|
+
var _a, _b, _c, _d, _e;
|
|
588
|
+
const org = config.organization;
|
|
589
|
+
if (!org) return null;
|
|
590
|
+
const schema = __spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
591
|
+
"@context": "https://schema.org",
|
|
592
|
+
"@type": (_a = org.type) != null ? _a : "Organization",
|
|
593
|
+
"@id": getOrganizationId(config),
|
|
594
|
+
name: (_b = org.name) != null ? _b : config.siteName,
|
|
595
|
+
url: (_c = org.url) != null ? _c : config.siteUrl.replace(/\/$/, "")
|
|
596
|
+
}, org.logo && {
|
|
597
|
+
logo: { "@type": "ImageObject", url: resolveEntityUrl(org.logo, config) }
|
|
598
|
+
}), ((_d = org.description) != null ? _d : config.description) && {
|
|
599
|
+
description: (_e = org.description) != null ? _e : config.description
|
|
600
|
+
}), org.sameAs && org.sameAs.length > 0 && { sameAs: org.sameAs }), org.parentOrganization && {
|
|
601
|
+
parentOrganization: {
|
|
602
|
+
"@type": "Organization",
|
|
603
|
+
name: org.parentOrganization.name,
|
|
604
|
+
url: org.parentOrganization.url
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
608
|
+
"script",
|
|
609
|
+
{
|
|
610
|
+
type: "application/ld+json",
|
|
611
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
|
|
612
|
+
}
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
function WebSiteSchema({ config }) {
|
|
616
|
+
const org = config.organization;
|
|
617
|
+
if (!org) return null;
|
|
618
|
+
const siteUrl = config.siteUrl.replace(/\/$/, "");
|
|
619
|
+
const template = org.searchUrlTemplate;
|
|
620
|
+
const schema = __spreadValues(__spreadValues({
|
|
621
|
+
"@context": "https://schema.org",
|
|
622
|
+
"@type": "WebSite",
|
|
623
|
+
"@id": getWebSiteId(config),
|
|
624
|
+
name: config.siteName,
|
|
625
|
+
url: siteUrl,
|
|
626
|
+
publisher: { "@id": getOrganizationId(config) }
|
|
627
|
+
}, config.description && { description: config.description }), template && {
|
|
628
|
+
potentialAction: {
|
|
629
|
+
"@type": "SearchAction",
|
|
630
|
+
target: {
|
|
631
|
+
"@type": "EntryPoint",
|
|
632
|
+
urlTemplate: resolveEntityUrl(template, config)
|
|
633
|
+
},
|
|
634
|
+
"query-input": "required name=search_term_string"
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
638
|
+
"script",
|
|
639
|
+
{
|
|
640
|
+
type: "application/ld+json",
|
|
641
|
+
dangerouslySetInnerHTML: { __html: JSON.stringify(schema) }
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
}
|
|
439
645
|
|
|
440
646
|
// src/ArticleSearchBar.tsx
|
|
441
647
|
var import_lucide_react = require("lucide-react");
|
|
@@ -901,26 +1107,6 @@ function LatestArticlesSection({
|
|
|
901
1107
|
] }) });
|
|
902
1108
|
}
|
|
903
1109
|
|
|
904
|
-
// src/articlesConfig.ts
|
|
905
|
-
var DEFAULT_PAGE_SIZE = 6;
|
|
906
|
-
var DEFAULT_CATEGORIES_PAGE_SIZE = 8;
|
|
907
|
-
var DEFAULT_LAYOUT = [
|
|
908
|
-
"hero",
|
|
909
|
-
"search",
|
|
910
|
-
"featured",
|
|
911
|
-
"latest",
|
|
912
|
-
"categories"
|
|
913
|
-
];
|
|
914
|
-
function breadcrumbsAreEnabled(config) {
|
|
915
|
-
var _a;
|
|
916
|
-
return config.breadcrumbs !== false && ((_a = config.breadcrumbs) == null ? void 0 : _a.show) !== false;
|
|
917
|
-
}
|
|
918
|
-
function getBreadcrumbsConfig(config) {
|
|
919
|
-
var _a;
|
|
920
|
-
if (config.breadcrumbs === false) return {};
|
|
921
|
-
return (_a = config.breadcrumbs) != null ? _a : {};
|
|
922
|
-
}
|
|
923
|
-
|
|
924
1110
|
// src/useArticles.ts
|
|
925
1111
|
var import_react3 = require("react");
|
|
926
1112
|
function useArticles(initialArticles, initialCategories) {
|
|
@@ -1623,17 +1809,28 @@ function CtaViewTracker({ ctaId, articleSlug, config, children }) {
|
|
|
1623
1809
|
|
|
1624
1810
|
// src/AuthorArticlesPage.tsx
|
|
1625
1811
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1626
|
-
function
|
|
1812
|
+
function getKnowsAbout(author, articles) {
|
|
1813
|
+
var _a;
|
|
1814
|
+
if ((_a = author.knowsAbout) == null ? void 0 : _a.length) return [...author.knowsAbout];
|
|
1815
|
+
return [...new Set(articles.flatMap((article) => {
|
|
1816
|
+
var _a2;
|
|
1817
|
+
return (_a2 = article.categories) != null ? _a2 : [];
|
|
1818
|
+
}))].filter(Boolean);
|
|
1819
|
+
}
|
|
1820
|
+
function getPersonSchema(author, config, articleCount, knowsAbout) {
|
|
1627
1821
|
var _a, _b;
|
|
1628
|
-
|
|
1822
|
+
const pageUrl = `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`;
|
|
1823
|
+
const identityUrl = (_a = getAuthorIdentityUrl(author, config)) != null ? _a : pageUrl;
|
|
1824
|
+
const authorUrl = (_b = author.url) != null ? _b : pageUrl;
|
|
1825
|
+
return __spreadProps(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
|
|
1629
1826
|
"@context": "https://schema.org",
|
|
1630
1827
|
"@type": "Person",
|
|
1828
|
+
"@id": getPersonId(identityUrl),
|
|
1631
1829
|
name: author.name,
|
|
1632
1830
|
description: author.bio,
|
|
1633
|
-
url:
|
|
1634
|
-
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), {
|
|
1635
|
-
|
|
1636
|
-
mainEntityOfPage: (_b = author.url) != null ? _b : `${config.siteUrl.replace(/\/$/, "")}/articles/authors/${author.slug}`,
|
|
1831
|
+
url: authorUrl
|
|
1832
|
+
}, getAuthorAvatar(author, config) && { image: getAuthorAvatar(author, config) }), getAuthorSameAs(author).length > 0 && { sameAs: getAuthorSameAs(author) }), knowsAbout.length > 0 && { knowsAbout: [...knowsAbout] }), config.organization && { worksFor: { "@id": getOrganizationId(config) } }), {
|
|
1833
|
+
mainEntityOfPage: pageUrl,
|
|
1637
1834
|
interactionStatistic: {
|
|
1638
1835
|
"@type": "InteractionCounter",
|
|
1639
1836
|
interactionType: "https://schema.org/WriteAction",
|
|
@@ -1786,7 +1983,9 @@ function AuthorArticlesPage({
|
|
|
1786
1983
|
{
|
|
1787
1984
|
type: "application/ld+json",
|
|
1788
1985
|
dangerouslySetInnerHTML: {
|
|
1789
|
-
__html: JSON.stringify(
|
|
1986
|
+
__html: JSON.stringify(
|
|
1987
|
+
getPersonSchema(author, config, articleCount, getKnowsAbout(author, articles))
|
|
1988
|
+
)
|
|
1790
1989
|
}
|
|
1791
1990
|
}
|
|
1792
1991
|
),
|
|
@@ -2117,10 +2316,32 @@ function ArticleTOC({ toc, className }) {
|
|
|
2117
2316
|
);
|
|
2118
2317
|
}
|
|
2119
2318
|
|
|
2319
|
+
// src/ArticleAnswer.tsx
|
|
2320
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2321
|
+
function ArticleAnswer({
|
|
2322
|
+
article,
|
|
2323
|
+
label = "The short answer",
|
|
2324
|
+
className
|
|
2325
|
+
}) {
|
|
2326
|
+
var _a;
|
|
2327
|
+
if (!((_a = article.answer) == null ? void 0 : _a.trim())) return null;
|
|
2328
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
2329
|
+
"aside",
|
|
2330
|
+
{
|
|
2331
|
+
className: `mb-8 rounded-lg border-l-4 border-primary bg-muted/40 px-6 py-4 ${className != null ? className : ""}`,
|
|
2332
|
+
style: { borderLeftWidth: "4px" },
|
|
2333
|
+
children: [
|
|
2334
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mb-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: label }),
|
|
2335
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-base leading-relaxed", children: article.answer })
|
|
2336
|
+
]
|
|
2337
|
+
}
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2120
2341
|
// src/ScrollToTop.tsx
|
|
2121
2342
|
var import_react7 = require("react");
|
|
2122
2343
|
var import_lucide_react11 = require("lucide-react");
|
|
2123
|
-
var
|
|
2344
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
2124
2345
|
function ScrollToTop() {
|
|
2125
2346
|
const [visible, setVisible] = (0, import_react7.useState)(false);
|
|
2126
2347
|
(0, import_react7.useEffect)(() => {
|
|
@@ -2129,13 +2350,13 @@ function ScrollToTop() {
|
|
|
2129
2350
|
return () => globalThis.removeEventListener("scroll", onScroll);
|
|
2130
2351
|
}, []);
|
|
2131
2352
|
if (!visible) return null;
|
|
2132
|
-
return /* @__PURE__ */ (0,
|
|
2353
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
2133
2354
|
"button",
|
|
2134
2355
|
{
|
|
2135
2356
|
"aria-label": "Scroll to top",
|
|
2136
2357
|
onClick: () => globalThis.scrollTo({ top: 0, behavior: "smooth" }),
|
|
2137
2358
|
className: "fixed bottom-8 right-8 z-50 rounded-full bg-primary p-2 shadow-md text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
2138
|
-
children: /* @__PURE__ */ (0,
|
|
2359
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.ArrowUp, { className: "h-5 w-5" })
|
|
2139
2360
|
}
|
|
2140
2361
|
);
|
|
2141
2362
|
}
|
|
@@ -2146,7 +2367,7 @@ var import_react11 = require("react");
|
|
|
2146
2367
|
|
|
2147
2368
|
// src/CommentForm.tsx
|
|
2148
2369
|
var import_react8 = require("react");
|
|
2149
|
-
var
|
|
2370
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2150
2371
|
var MAX_LENGTH = 2e3;
|
|
2151
2372
|
var WARN_THRESHOLD = 1800;
|
|
2152
2373
|
function submitLabel(submitting, parentId) {
|
|
@@ -2185,8 +2406,8 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2185
2406
|
}
|
|
2186
2407
|
});
|
|
2187
2408
|
}
|
|
2188
|
-
return /* @__PURE__ */ (0,
|
|
2189
|
-
/* @__PURE__ */ (0,
|
|
2409
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
|
|
2410
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2190
2411
|
"textarea",
|
|
2191
2412
|
{
|
|
2192
2413
|
value: body,
|
|
@@ -2199,13 +2420,13 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2199
2420
|
"aria-label": parentId ? "Reply text" : "Comment text"
|
|
2200
2421
|
}
|
|
2201
2422
|
),
|
|
2202
|
-
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0,
|
|
2423
|
+
remaining <= MAX_LENGTH - WARN_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { className: `text-xs ${remaining < 0 ? "text-destructive" : "text-muted-foreground"}`, children: [
|
|
2203
2424
|
remaining,
|
|
2204
2425
|
" characters remaining"
|
|
2205
2426
|
] }),
|
|
2206
|
-
error && /* @__PURE__ */ (0,
|
|
2207
|
-
/* @__PURE__ */ (0,
|
|
2208
|
-
/* @__PURE__ */ (0,
|
|
2427
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-xs text-destructive", children: error }),
|
|
2428
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex gap-2", children: [
|
|
2429
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2209
2430
|
"button",
|
|
2210
2431
|
{
|
|
2211
2432
|
type: "submit",
|
|
@@ -2214,7 +2435,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2214
2435
|
children: submitLabel(submitting, parentId)
|
|
2215
2436
|
}
|
|
2216
2437
|
),
|
|
2217
|
-
onCancel && /* @__PURE__ */ (0,
|
|
2438
|
+
onCancel && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
2218
2439
|
"button",
|
|
2219
2440
|
{
|
|
2220
2441
|
type: "button",
|
|
@@ -2229,7 +2450,7 @@ function CommentForm({ articleSlug, parentId, onSuccess, onCancel }) {
|
|
|
2229
2450
|
|
|
2230
2451
|
// src/CommentItem.tsx
|
|
2231
2452
|
var import_react9 = require("react");
|
|
2232
|
-
var
|
|
2453
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
2233
2454
|
function relativeTime(iso) {
|
|
2234
2455
|
const diff = Date.now() - new Date(iso).getTime();
|
|
2235
2456
|
const minutes = Math.floor(diff / 6e4);
|
|
@@ -2267,15 +2488,15 @@ function CommentItem({
|
|
|
2267
2488
|
}
|
|
2268
2489
|
});
|
|
2269
2490
|
}
|
|
2270
|
-
return /* @__PURE__ */ (0,
|
|
2271
|
-
/* @__PURE__ */ (0,
|
|
2272
|
-
/* @__PURE__ */ (0,
|
|
2273
|
-
/* @__PURE__ */ (0,
|
|
2274
|
-
/* @__PURE__ */ (0,
|
|
2491
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: `${isReply ? "ml-8 border-l-2 border-border pl-4" : ""}`, children: [
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "rounded-lg bg-muted/40 p-3 space-y-1", children: comment.isDeleted ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm italic text-muted-foreground", children: "Comment removed" }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2494
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "text-sm font-medium text-foreground", children: comment.authorName }),
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "text-xs text-muted-foreground", children: relativeTime(comment.createdAt) })
|
|
2275
2496
|
] }),
|
|
2276
|
-
/* @__PURE__ */ (0,
|
|
2277
|
-
/* @__PURE__ */ (0,
|
|
2278
|
-
canReply && /* @__PURE__ */ (0,
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm text-foreground whitespace-pre-wrap break-words", children: comment.body }),
|
|
2498
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex gap-3 pt-1", children: [
|
|
2499
|
+
canReply && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2279
2500
|
"button",
|
|
2280
2501
|
{
|
|
2281
2502
|
onClick: () => setShowReplyForm((v) => !v),
|
|
@@ -2283,7 +2504,7 @@ function CommentItem({
|
|
|
2283
2504
|
children: showReplyForm ? "Cancel" : "Reply"
|
|
2284
2505
|
}
|
|
2285
2506
|
),
|
|
2286
|
-
canDelete && /* @__PURE__ */ (0,
|
|
2507
|
+
canDelete && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2287
2508
|
"button",
|
|
2288
2509
|
{
|
|
2289
2510
|
onClick: handleDelete,
|
|
@@ -2294,7 +2515,7 @@ function CommentItem({
|
|
|
2294
2515
|
)
|
|
2295
2516
|
] })
|
|
2296
2517
|
] }) }),
|
|
2297
|
-
showReplyForm && /* @__PURE__ */ (0,
|
|
2518
|
+
showReplyForm && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "mt-2 ml-2", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2298
2519
|
CommentForm,
|
|
2299
2520
|
{
|
|
2300
2521
|
articleSlug,
|
|
@@ -2306,7 +2527,7 @@ function CommentItem({
|
|
|
2306
2527
|
onCancel: () => setShowReplyForm(false)
|
|
2307
2528
|
}
|
|
2308
2529
|
) }),
|
|
2309
|
-
replies.length > 0 && /* @__PURE__ */ (0,
|
|
2530
|
+
replies.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "mt-2 space-y-2", children: replies.map((reply) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
2310
2531
|
CommentItem,
|
|
2311
2532
|
{
|
|
2312
2533
|
comment: __spreadProps(__spreadValues({}, reply), { replies: [] }),
|
|
@@ -2321,7 +2542,7 @@ function CommentItem({
|
|
|
2321
2542
|
}
|
|
2322
2543
|
|
|
2323
2544
|
// src/CommentThread.tsx
|
|
2324
|
-
var
|
|
2545
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
2325
2546
|
function CommentThread({
|
|
2326
2547
|
comments,
|
|
2327
2548
|
articleSlug,
|
|
@@ -2329,9 +2550,9 @@ function CommentThread({
|
|
|
2329
2550
|
onRefresh
|
|
2330
2551
|
}) {
|
|
2331
2552
|
if (comments.length === 0) {
|
|
2332
|
-
return /* @__PURE__ */ (0,
|
|
2553
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-sm text-muted-foreground text-center py-6", children: "No comments yet. Be the first to share your thoughts." });
|
|
2333
2554
|
}
|
|
2334
|
-
return /* @__PURE__ */ (0,
|
|
2555
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
2335
2556
|
CommentItem,
|
|
2336
2557
|
{
|
|
2337
2558
|
comment,
|
|
@@ -2344,7 +2565,7 @@ function CommentThread({
|
|
|
2344
2565
|
}
|
|
2345
2566
|
|
|
2346
2567
|
// src/CommentsSection.tsx
|
|
2347
|
-
var
|
|
2568
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
2348
2569
|
function CommentsSection({ articleSlug, config }) {
|
|
2349
2570
|
var _a, _b, _c, _d, _e, _f;
|
|
2350
2571
|
const { data: session, status } = (0, import_react10.useSession)();
|
|
@@ -2376,10 +2597,10 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
2376
2597
|
if (!enabled) return null;
|
|
2377
2598
|
const count = comments.length;
|
|
2378
2599
|
const label = count === 1 ? "1 comment" : `${count} comments`;
|
|
2379
|
-
return /* @__PURE__ */ (0,
|
|
2380
|
-
/* @__PURE__ */ (0,
|
|
2381
|
-
status === "authenticated" ? /* @__PURE__ */ (0,
|
|
2382
|
-
/* @__PURE__ */ (0,
|
|
2600
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("section", { "aria-label": "Comments", className: "mt-12 pt-8 border-t border-border space-y-6", children: [
|
|
2601
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("h2", { className: "text-xl font-semibold text-foreground", children: loading ? "Comments" : label }),
|
|
2602
|
+
status === "authenticated" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommentForm, { articleSlug, onSuccess: fetchComments }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { className: "text-sm text-muted-foreground", children: [
|
|
2603
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2383
2604
|
"button",
|
|
2384
2605
|
{
|
|
2385
2606
|
onClick: () => (0, import_react10.signIn)(),
|
|
@@ -2390,8 +2611,8 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
2390
2611
|
" ",
|
|
2391
2612
|
"to join the discussion."
|
|
2392
2613
|
] }),
|
|
2393
|
-
fetchError && /* @__PURE__ */ (0,
|
|
2394
|
-
loading ? /* @__PURE__ */ (0,
|
|
2614
|
+
fetchError && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-sm text-destructive", children: fetchError }),
|
|
2615
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-sm text-muted-foreground", children: "Loading comments\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2395
2616
|
CommentThread,
|
|
2396
2617
|
{
|
|
2397
2618
|
comments,
|
|
@@ -2404,6 +2625,7 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
2404
2625
|
}
|
|
2405
2626
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2406
2627
|
0 && (module.exports = {
|
|
2628
|
+
ArticleAnswer,
|
|
2407
2629
|
ArticleBackLink,
|
|
2408
2630
|
ArticleCard,
|
|
2409
2631
|
ArticleCategoryGrid,
|
|
@@ -2432,16 +2654,23 @@ function CommentsSection({ articleSlug, config }) {
|
|
|
2432
2654
|
DEFAULT_CATEGORIES_PAGE_SIZE,
|
|
2433
2655
|
DEFAULT_LAYOUT,
|
|
2434
2656
|
DEFAULT_PAGE_SIZE,
|
|
2657
|
+
DEFAULT_TITLE_TEMPLATE,
|
|
2435
2658
|
FAQPageSchema,
|
|
2436
2659
|
FeaturedArticle,
|
|
2437
2660
|
LatestArticles,
|
|
2438
2661
|
LatestArticlesSection,
|
|
2662
|
+
OrganizationSchema,
|
|
2439
2663
|
PaginationNav,
|
|
2440
2664
|
RelatedArticlesSection,
|
|
2441
2665
|
ScrollToTop,
|
|
2442
2666
|
SeriesArticlesPage,
|
|
2667
|
+
WebSiteSchema,
|
|
2443
2668
|
breadcrumbsAreEnabled,
|
|
2669
|
+
formatPageTitle,
|
|
2444
2670
|
getBreadcrumbsConfig,
|
|
2671
|
+
getOrganizationId,
|
|
2672
|
+
getPersonId,
|
|
2673
|
+
getWebSiteId,
|
|
2445
2674
|
useArticles
|
|
2446
2675
|
});
|
|
2447
2676
|
//# sourceMappingURL=index.cjs.map
|