@zeropress/build-core 0.1.3 → 0.1.4
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 +11 -0
- package/package.json +2 -2
- package/src/api/build-site.js +59 -12
package/README.md
CHANGED
|
@@ -205,6 +205,17 @@ Build-core now derives:
|
|
|
205
205
|
- `reading_time`
|
|
206
206
|
- `comments_html`
|
|
207
207
|
|
|
208
|
+
Comment shell rendering is derived from preview-data policy:
|
|
209
|
+
|
|
210
|
+
- `site.disallowComments`
|
|
211
|
+
- `content.posts[].allow_comments`
|
|
212
|
+
|
|
213
|
+
The canonical `preview-data v0.4` site contract uses:
|
|
214
|
+
|
|
215
|
+
- `site.locale`
|
|
216
|
+
- `site.timezone`
|
|
217
|
+
- `site.disallowComments`
|
|
218
|
+
|
|
208
219
|
Optional route templates behave as rendering capabilities, not guaranteed outputs:
|
|
209
220
|
|
|
210
221
|
- `archive.html`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeropress/build-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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.
|
|
36
|
+
"@zeropress/preview-data-validator": "^0.3.1",
|
|
37
37
|
"@zeropress/theme-validator": "^0.2.2"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/src/api/build-site.js
CHANGED
|
@@ -16,8 +16,6 @@ const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD';
|
|
|
16
16
|
const DEFAULT_TIME_FORMAT = 'HH:mm';
|
|
17
17
|
const DEFAULT_TIMEZONE = 'UTC';
|
|
18
18
|
const DEFAULT_LOCALE = 'en-US';
|
|
19
|
-
const COMMENTS_HTML = '<section id="comments"></section>';
|
|
20
|
-
|
|
21
19
|
export async function buildSite(input) {
|
|
22
20
|
const options = { ...DEFAULT_OPTIONS, ...(input.options || {}) };
|
|
23
21
|
const state = await createBuildState(input, options);
|
|
@@ -58,8 +56,10 @@ export async function buildSite(input) {
|
|
|
58
56
|
|
|
59
57
|
if (options.generateSpecialFiles) {
|
|
60
58
|
await maybeRenderNotFoundPage(state);
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
if (hasCanonicalSiteUrl(state.previewData.site.url)) {
|
|
60
|
+
await writeOutput(state.writer, state.summaries, 'sitemap.xml', buildSitemapXml(state.previewData.site, state.emitted, state.generatedAt), 'application/xml');
|
|
61
|
+
await writeOutput(state.writer, state.summaries, 'feed.xml', buildFeedXml(state.previewData.site, state.emitted, state.generatedAt), 'application/rss+xml');
|
|
62
|
+
}
|
|
63
63
|
await writeOutput(state.writer, state.summaries, 'robots.txt', buildRobotsTxt(state.previewData.site), 'text/plain');
|
|
64
64
|
await writeOutput(state.writer, state.summaries, 'meta.json', buildMetaJson(state.previewData.site, state.emitted, state.generatedAt), 'application/json');
|
|
65
65
|
}
|
|
@@ -278,8 +278,9 @@ function normalizePreviewData(previewData) {
|
|
|
278
278
|
: DEFAULT_POSTS_PER_PAGE,
|
|
279
279
|
dateFormat: normalizeNonEmptyString(previewData.site.dateFormat, DEFAULT_DATE_FORMAT),
|
|
280
280
|
timeFormat: typeof previewData.site.timeFormat === 'string' ? previewData.site.timeFormat : DEFAULT_TIME_FORMAT,
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
timezone: normalizeNonEmptyString(previewData.site.timezone, DEFAULT_TIMEZONE),
|
|
282
|
+
locale: normalizeLocale(previewData.site.locale || DEFAULT_LOCALE),
|
|
283
|
+
disallowComments: previewData.site.disallowComments === true,
|
|
283
284
|
},
|
|
284
285
|
content: {
|
|
285
286
|
...previewData.content,
|
|
@@ -370,10 +371,52 @@ function preparePost(post, site, categoriesBySlug, tagsBySlug) {
|
|
|
370
371
|
reading_time: calculateReadingTime(post.html),
|
|
371
372
|
categories_html: renderInlineTaxonomyLinks(post.category_slugs, categoriesBySlug, 'category'),
|
|
372
373
|
tags_html: renderInlineTaxonomyLinks(post.tag_slugs, tagsBySlug, 'tag'),
|
|
373
|
-
comments_html:
|
|
374
|
+
comments_html: renderCommentsContainer(site, post),
|
|
374
375
|
};
|
|
375
376
|
}
|
|
376
377
|
|
|
378
|
+
function renderCommentsContainer(site, post) {
|
|
379
|
+
if (site.disallowComments === true || post.allow_comments !== true) {
|
|
380
|
+
return '';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const safePostId = encodeURIComponent(post.id);
|
|
384
|
+
return `<section id="comments" class="zp-comments">
|
|
385
|
+
<h2>댓글</h2>
|
|
386
|
+
<div id="comment-list"
|
|
387
|
+
hx-get="/api/comments/${safePostId}"
|
|
388
|
+
hx-trigger="load"
|
|
389
|
+
hx-swap="innerHTML">
|
|
390
|
+
<p class="zp-comments-loading">댓글을 불러오는 중...</p>
|
|
391
|
+
</div>
|
|
392
|
+
<form id="comment-form"
|
|
393
|
+
hx-post="/api/comments/${safePostId}"
|
|
394
|
+
hx-target="#comment-form-result"
|
|
395
|
+
hx-swap="innerHTML">
|
|
396
|
+
<div id="comment-form-result"></div>
|
|
397
|
+
<div>
|
|
398
|
+
<label for="author_name">이름</label>
|
|
399
|
+
<input type="text" id="author_name" name="author_name" required>
|
|
400
|
+
</div>
|
|
401
|
+
<div>
|
|
402
|
+
<label for="author_email">이메일</label>
|
|
403
|
+
<input type="email" id="author_email" name="author_email" required>
|
|
404
|
+
</div>
|
|
405
|
+
<div>
|
|
406
|
+
<label for="content">댓글</label>
|
|
407
|
+
<textarea id="content" name="content" required minlength="1" maxlength="5000"></textarea>
|
|
408
|
+
</div>
|
|
409
|
+
<div style="position:absolute;left:-9999px;" aria-hidden="true">
|
|
410
|
+
<input type="text" name="website" tabindex="-1" autocomplete="off">
|
|
411
|
+
</div>
|
|
412
|
+
<button type="submit">댓글 작성</button>
|
|
413
|
+
</form>
|
|
414
|
+
<noscript>
|
|
415
|
+
<p>댓글 기능을 사용하려면 JavaScript를 활성화해 주세요.</p>
|
|
416
|
+
</noscript>
|
|
417
|
+
</section>`;
|
|
418
|
+
}
|
|
419
|
+
|
|
377
420
|
function buildTaxonomyRoutes(options) {
|
|
378
421
|
const routes = [];
|
|
379
422
|
|
|
@@ -590,10 +633,10 @@ function renderPagination(paginationData) {
|
|
|
590
633
|
|
|
591
634
|
function formatTimestamp(value, site) {
|
|
592
635
|
const date = toDate(value);
|
|
593
|
-
const locale = normalizeLocale(site.
|
|
636
|
+
const locale = normalizeLocale(site.locale || DEFAULT_LOCALE);
|
|
594
637
|
const dateFormat = normalizeNonEmptyString(site.dateFormat, DEFAULT_DATE_FORMAT);
|
|
595
638
|
const timeFormat = typeof site.timeFormat === 'string' ? site.timeFormat : DEFAULT_TIME_FORMAT;
|
|
596
|
-
const siteTimezone = normalizeNonEmptyString(site.
|
|
639
|
+
const siteTimezone = normalizeNonEmptyString(site.timezone, DEFAULT_TIMEZONE);
|
|
597
640
|
|
|
598
641
|
const dateParts = new Intl.DateTimeFormat(locale, {
|
|
599
642
|
timeZone: siteTimezone,
|
|
@@ -773,7 +816,7 @@ function createRenderContext(site, currentUrl) {
|
|
|
773
816
|
return {
|
|
774
817
|
site,
|
|
775
818
|
currentUrl,
|
|
776
|
-
language: site.
|
|
819
|
+
language: site.locale,
|
|
777
820
|
};
|
|
778
821
|
}
|
|
779
822
|
|
|
@@ -879,7 +922,7 @@ function buildFeedXml(site, emitted, generatedAt) {
|
|
|
879
922
|
})
|
|
880
923
|
.join('\n');
|
|
881
924
|
|
|
882
|
-
return `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n <channel>\n <title>${escapeXml(site.title)}</title>\n <link>${escapeXml(channelLink)}</link>\n <description>${escapeXml(site.description)}</description>\n <language>${site.
|
|
925
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n <channel>\n <title>${escapeXml(site.title)}</title>\n <link>${escapeXml(channelLink)}</link>\n <description>${escapeXml(site.description)}</description>\n <language>${site.locale}</language>\n <lastBuildDate>${generatedAt.toUTCString()}</lastBuildDate>\n <atom:link href="${escapeXml(selfLink)}" rel="self" type="application/rss+xml" />\n${items}\n </channel>\n</rss>`;
|
|
883
926
|
}
|
|
884
927
|
|
|
885
928
|
function buildRobotsTxt(site) {
|
|
@@ -915,7 +958,7 @@ function buildMetaJson(site, emitted, generatedAt) {
|
|
|
915
958
|
title: site.title,
|
|
916
959
|
description: site.description,
|
|
917
960
|
url: site.url,
|
|
918
|
-
|
|
961
|
+
locale: site.locale,
|
|
919
962
|
},
|
|
920
963
|
pages,
|
|
921
964
|
}, null, 2);
|
|
@@ -983,6 +1026,10 @@ function resolveSiteUrl(siteUrl, relativePath) {
|
|
|
983
1026
|
return decodeURI(new URL(relativePath, siteUrl).toString());
|
|
984
1027
|
}
|
|
985
1028
|
|
|
1029
|
+
function hasCanonicalSiteUrl(siteUrl) {
|
|
1030
|
+
return typeof siteUrl === 'string' && siteUrl.trim() !== '';
|
|
1031
|
+
}
|
|
1032
|
+
|
|
986
1033
|
function toDate(value) {
|
|
987
1034
|
return value ? new Date(value) : new Date();
|
|
988
1035
|
}
|