@zeropress/build-core 0.1.5 → 0.1.7
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/package.json +3 -3
- package/src/api/build-site.js +196 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeropress/build-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Shared ZeroPress build and partial-render core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"node": ">=18.18.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@zeropress/preview-data-validator": "^0.3.
|
|
37
|
-
"@zeropress/theme-validator": "^0.2.
|
|
36
|
+
"@zeropress/preview-data-validator": "^0.3.3",
|
|
37
|
+
"@zeropress/theme-validator": "^0.2.4"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/src/api/build-site.js
CHANGED
|
@@ -192,7 +192,15 @@ async function renderRoute(state, templateName, route) {
|
|
|
192
192
|
const currentUrl = normalizeRoutePath(route.path);
|
|
193
193
|
let html = await state.engine.render(
|
|
194
194
|
templateName,
|
|
195
|
-
{
|
|
195
|
+
{
|
|
196
|
+
...route,
|
|
197
|
+
meta: buildPageMeta(state.previewData.site, {
|
|
198
|
+
currentUrl,
|
|
199
|
+
title: state.previewData.site.title,
|
|
200
|
+
description: state.previewData.site.description,
|
|
201
|
+
ogType: 'website',
|
|
202
|
+
}),
|
|
203
|
+
},
|
|
196
204
|
createRenderContext(state.previewData.site, currentUrl),
|
|
197
205
|
);
|
|
198
206
|
html = state.assetProcessor.updateAssetReferences(html, state.assetMap);
|
|
@@ -204,7 +212,18 @@ async function renderPost(state, post) {
|
|
|
204
212
|
const currentUrl = `/posts/${encodeSlugSegment(post.slug)}/`;
|
|
205
213
|
let html = await state.engine.render(
|
|
206
214
|
'post',
|
|
207
|
-
{
|
|
215
|
+
{
|
|
216
|
+
post,
|
|
217
|
+
meta: buildPageMeta(state.previewData.site, {
|
|
218
|
+
currentUrl,
|
|
219
|
+
title: buildDocumentTitle(post.title, state.previewData.site.title),
|
|
220
|
+
description: post.excerpt,
|
|
221
|
+
ogType: 'article',
|
|
222
|
+
image: post.featured_image,
|
|
223
|
+
publishedTime: post.published_at_iso,
|
|
224
|
+
modifiedTime: post.updated_at_iso,
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
208
227
|
createRenderContext(state.previewData.site, currentUrl),
|
|
209
228
|
);
|
|
210
229
|
html = state.assetProcessor.updateAssetReferences(html, state.assetMap);
|
|
@@ -223,16 +242,27 @@ async function renderPost(state, post) {
|
|
|
223
242
|
}
|
|
224
243
|
|
|
225
244
|
async function renderPage(state, page) {
|
|
245
|
+
const currentUrl = `/${encodeSlugSegment(page.slug)}/`;
|
|
226
246
|
let html = await state.engine.render(
|
|
227
247
|
'page',
|
|
228
|
-
{
|
|
229
|
-
|
|
248
|
+
{
|
|
249
|
+
page,
|
|
250
|
+
meta: buildPageMeta(state.previewData.site, {
|
|
251
|
+
currentUrl,
|
|
252
|
+
title: buildDocumentTitle(page.title, state.previewData.site.title),
|
|
253
|
+
description: page.excerpt,
|
|
254
|
+
ogType: 'website',
|
|
255
|
+
image: page.featured_image,
|
|
256
|
+
}),
|
|
257
|
+
},
|
|
258
|
+
createRenderContext(state.previewData.site, currentUrl),
|
|
230
259
|
);
|
|
231
260
|
html = state.assetProcessor.updateAssetReferences(html, state.assetMap);
|
|
232
261
|
await writeOutput(state.writer, state.summaries, `${encodeSlugSegment(page.slug)}/index.html`, html, 'text/html');
|
|
233
262
|
state.emitted.pages.push({
|
|
234
|
-
url:
|
|
263
|
+
url: currentUrl,
|
|
235
264
|
title: page.title,
|
|
265
|
+
description: page.excerpt,
|
|
236
266
|
status: page.status,
|
|
237
267
|
});
|
|
238
268
|
}
|
|
@@ -244,7 +274,14 @@ async function maybeRenderNotFoundPage(state) {
|
|
|
244
274
|
|
|
245
275
|
let html = await state.engine.render(
|
|
246
276
|
'404',
|
|
247
|
-
{
|
|
277
|
+
{
|
|
278
|
+
meta: buildPageMeta(state.previewData.site, {
|
|
279
|
+
currentUrl: '/404.html',
|
|
280
|
+
title: state.previewData.site.title,
|
|
281
|
+
description: state.previewData.site.description,
|
|
282
|
+
ogType: 'website',
|
|
283
|
+
}),
|
|
284
|
+
},
|
|
248
285
|
createRenderContext(state.previewData.site, '/404.html'),
|
|
249
286
|
);
|
|
250
287
|
html = state.assetProcessor.updateAssetReferences(html, state.assetMap);
|
|
@@ -269,23 +306,35 @@ function validateSelection(selection) {
|
|
|
269
306
|
}
|
|
270
307
|
|
|
271
308
|
function normalizePreviewData(previewData) {
|
|
309
|
+
const normalizedSite = {
|
|
310
|
+
...previewData.site,
|
|
311
|
+
mediaBaseUrl: normalizeOptionalString(previewData.site.mediaBaseUrl),
|
|
312
|
+
postsPerPage: Number.isInteger(previewData.site.postsPerPage) && previewData.site.postsPerPage > 0
|
|
313
|
+
? previewData.site.postsPerPage
|
|
314
|
+
: DEFAULT_POSTS_PER_PAGE,
|
|
315
|
+
dateFormat: normalizeNonEmptyString(previewData.site.dateFormat, DEFAULT_DATE_FORMAT),
|
|
316
|
+
timeFormat: typeof previewData.site.timeFormat === 'string' ? previewData.site.timeFormat : DEFAULT_TIME_FORMAT,
|
|
317
|
+
timezone: normalizeNonEmptyString(previewData.site.timezone, DEFAULT_TIMEZONE),
|
|
318
|
+
locale: normalizeLocale(previewData.site.locale || DEFAULT_LOCALE),
|
|
319
|
+
disallowComments: previewData.site.disallowComments === true,
|
|
320
|
+
};
|
|
321
|
+
|
|
272
322
|
return {
|
|
273
323
|
...previewData,
|
|
274
|
-
site:
|
|
275
|
-
...previewData.site,
|
|
276
|
-
postsPerPage: Number.isInteger(previewData.site.postsPerPage) && previewData.site.postsPerPage > 0
|
|
277
|
-
? previewData.site.postsPerPage
|
|
278
|
-
: DEFAULT_POSTS_PER_PAGE,
|
|
279
|
-
dateFormat: normalizeNonEmptyString(previewData.site.dateFormat, DEFAULT_DATE_FORMAT),
|
|
280
|
-
timeFormat: typeof previewData.site.timeFormat === 'string' ? previewData.site.timeFormat : DEFAULT_TIME_FORMAT,
|
|
281
|
-
timezone: normalizeNonEmptyString(previewData.site.timezone, DEFAULT_TIMEZONE),
|
|
282
|
-
locale: normalizeLocale(previewData.site.locale || DEFAULT_LOCALE),
|
|
283
|
-
disallowComments: previewData.site.disallowComments === true,
|
|
284
|
-
},
|
|
324
|
+
site: normalizedSite,
|
|
285
325
|
content: {
|
|
286
326
|
...previewData.content,
|
|
287
|
-
posts:
|
|
288
|
-
|
|
327
|
+
posts: previewData.content.posts
|
|
328
|
+
.map((post) => ({
|
|
329
|
+
...post,
|
|
330
|
+
author_avatar: normalizeMediaField(post.author_avatar, normalizedSite.mediaBaseUrl),
|
|
331
|
+
featured_image: normalizeMediaField(post.featured_image, normalizedSite.mediaBaseUrl),
|
|
332
|
+
}))
|
|
333
|
+
.sort((left, right) => toDate(right.published_at_iso).getTime() - toDate(left.published_at_iso).getTime()),
|
|
334
|
+
pages: previewData.content.pages.map((page) => ({
|
|
335
|
+
...page,
|
|
336
|
+
featured_image: normalizeMediaField(page.featured_image, normalizedSite.mediaBaseUrl),
|
|
337
|
+
})),
|
|
289
338
|
categories: [...previewData.content.categories],
|
|
290
339
|
tags: [...previewData.content.tags],
|
|
291
340
|
},
|
|
@@ -697,6 +746,10 @@ function normalizeNonEmptyString(value, fallback) {
|
|
|
697
746
|
return typeof value === 'string' && value.trim() ? value : fallback;
|
|
698
747
|
}
|
|
699
748
|
|
|
749
|
+
function normalizeOptionalString(value) {
|
|
750
|
+
return typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
751
|
+
}
|
|
752
|
+
|
|
700
753
|
function normalizeLocale(value) {
|
|
701
754
|
if (typeof value !== 'string' || !value.trim()) {
|
|
702
755
|
return DEFAULT_LOCALE;
|
|
@@ -820,6 +873,129 @@ function createRenderContext(site, currentUrl) {
|
|
|
820
873
|
};
|
|
821
874
|
}
|
|
822
875
|
|
|
876
|
+
function buildPageMeta(site, options = {}) {
|
|
877
|
+
const resolvedTitle = normalizeNonEmptyString(options.title, site.title);
|
|
878
|
+
const resolvedDescription = normalizeOptionalString(options.description);
|
|
879
|
+
const canonicalUrl = resolveMetaCanonicalUrl(site, options.currentUrl);
|
|
880
|
+
const ogImage = resolveMetaImageUrl(options.image);
|
|
881
|
+
const ogType = normalizeNonEmptyString(options.ogType, 'website');
|
|
882
|
+
const publishedTime = normalizeOptionalString(options.publishedTime);
|
|
883
|
+
const modifiedTime = normalizeOptionalString(options.modifiedTime);
|
|
884
|
+
|
|
885
|
+
const meta = {
|
|
886
|
+
title: escapeHtml(resolvedTitle),
|
|
887
|
+
description: resolvedDescription ? escapeHtml(resolvedDescription) : '',
|
|
888
|
+
canonical_url: canonicalUrl ? escapeHtml(canonicalUrl) : '',
|
|
889
|
+
og_title: escapeHtml(resolvedTitle),
|
|
890
|
+
og_description: resolvedDescription ? escapeHtml(resolvedDescription) : '',
|
|
891
|
+
og_type: escapeHtml(ogType),
|
|
892
|
+
og_url: canonicalUrl ? escapeHtml(canonicalUrl) : '',
|
|
893
|
+
og_site_name: escapeHtml(site.title),
|
|
894
|
+
og_image: ogImage ? escapeHtml(ogImage) : '',
|
|
895
|
+
article_published_time: publishedTime ? escapeHtml(publishedTime) : '',
|
|
896
|
+
article_modified_time: modifiedTime ? escapeHtml(modifiedTime) : '',
|
|
897
|
+
};
|
|
898
|
+
|
|
899
|
+
return {
|
|
900
|
+
...meta,
|
|
901
|
+
head_tags: buildMetaHeadTags(meta),
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function buildDocumentTitle(contentTitle, siteTitle) {
|
|
906
|
+
const resolvedContentTitle = normalizeNonEmptyString(contentTitle, siteTitle);
|
|
907
|
+
const resolvedSiteTitle = normalizeNonEmptyString(siteTitle, resolvedContentTitle);
|
|
908
|
+
return `${resolvedContentTitle} - ${resolvedSiteTitle}`;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function buildMetaHeadTags(meta) {
|
|
912
|
+
const tags = [];
|
|
913
|
+
|
|
914
|
+
if (meta.description) {
|
|
915
|
+
tags.push(`<meta name="description" content="${meta.description}">`);
|
|
916
|
+
}
|
|
917
|
+
if (meta.canonical_url) {
|
|
918
|
+
tags.push(`<link rel="canonical" href="${meta.canonical_url}">`);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
tags.push(`<meta property="og:title" content="${meta.og_title}">`);
|
|
922
|
+
if (meta.og_description) {
|
|
923
|
+
tags.push(`<meta property="og:description" content="${meta.og_description}">`);
|
|
924
|
+
}
|
|
925
|
+
tags.push(`<meta property="og:type" content="${meta.og_type}">`);
|
|
926
|
+
if (meta.og_url) {
|
|
927
|
+
tags.push(`<meta property="og:url" content="${meta.og_url}">`);
|
|
928
|
+
}
|
|
929
|
+
tags.push(`<meta property="og:site_name" content="${meta.og_site_name}">`);
|
|
930
|
+
if (meta.og_image) {
|
|
931
|
+
tags.push(`<meta property="og:image" content="${meta.og_image}">`);
|
|
932
|
+
}
|
|
933
|
+
if (meta.article_published_time) {
|
|
934
|
+
tags.push(`<meta property="article:published_time" content="${meta.article_published_time}">`);
|
|
935
|
+
}
|
|
936
|
+
if (meta.article_modified_time) {
|
|
937
|
+
tags.push(`<meta property="article:modified_time" content="${meta.article_modified_time}">`);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
return tags.length ? `${tags.join('\n')}\n` : '';
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function resolveMetaCanonicalUrl(site, currentUrl) {
|
|
944
|
+
if (!hasCanonicalSiteUrl(site.url) || !normalizeOptionalString(currentUrl)) {
|
|
945
|
+
return '';
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
return resolveSiteUrl(site.url, currentUrl);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function resolveMetaImageUrl(image) {
|
|
952
|
+
const normalizedImage = normalizeOptionalString(image);
|
|
953
|
+
if (!normalizedImage) {
|
|
954
|
+
return '';
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
if (isAbsoluteUrl(normalizedImage)) {
|
|
958
|
+
return normalizedImage;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return '';
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function normalizeMediaField(value, mediaBaseUrl) {
|
|
965
|
+
if (value === undefined) {
|
|
966
|
+
return undefined;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const normalizedValue = normalizeOptionalString(value);
|
|
970
|
+
if (!normalizedValue) {
|
|
971
|
+
return '';
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
if (isAbsoluteUrl(normalizedValue)) {
|
|
975
|
+
return normalizedValue;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
const normalizedBaseUrl = normalizeOptionalString(mediaBaseUrl);
|
|
979
|
+
if (!normalizedBaseUrl) {
|
|
980
|
+
return '';
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
try {
|
|
984
|
+
return decodeURI(new URL(normalizedValue, normalizedBaseUrl).toString());
|
|
985
|
+
} catch {
|
|
986
|
+
return '';
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function isAbsoluteUrl(value) {
|
|
991
|
+
try {
|
|
992
|
+
new URL(value);
|
|
993
|
+
return true;
|
|
994
|
+
} catch {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
823
999
|
async function writeOutput(writer, summaries, path, content, contentType) {
|
|
824
1000
|
const normalizedPath = normalizeOutputPath(path);
|
|
825
1001
|
await writer.write({ path: normalizedPath, content, contentType });
|
|
@@ -947,6 +1123,7 @@ function buildMetaJson(site, emitted, generatedAt) {
|
|
|
947
1123
|
...emitted.pages.map((page) => ({
|
|
948
1124
|
url: page.url,
|
|
949
1125
|
title: page.title,
|
|
1126
|
+
description: page.description,
|
|
950
1127
|
type: 'page',
|
|
951
1128
|
metadata: { status: page.status },
|
|
952
1129
|
})),
|