@zeropress/build-core 0.6.5 → 0.6.8

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 CHANGED
@@ -120,7 +120,8 @@ Notes:
120
120
  - `writer` is required
121
121
  - `previewData` must already satisfy the canonical preview-data contract
122
122
  - `themePackage` must already be a validated in-memory theme package
123
- - `sitemap.xml` and `feed.xml` are emitted only when `site.url` is a non-empty canonical URL
123
+ - `sitemap.xml` is emitted only when `site.url` is a non-empty canonical URL
124
+ - `feed.xml` is emitted only when `site.url` is a non-empty canonical URL and `generateFeed` is not `false`
124
125
  - callers may pass `sitemapStylesheetHref` to add an XML stylesheet processing instruction to generated `sitemap.xml`
125
126
  - fallback `robots.txt` is emitted when `generateSpecialFiles` is enabled and `generateRobotsTxt` is not `false`
126
127
  - fallback `robots.txt` uses `site.indexing`; `false` emits `Disallow: /`, while missing or `true` emits `Allow: /`
@@ -229,6 +230,7 @@ Supported options:
229
230
  - `favicon`
230
231
  - `sitemapStylesheetHref`
231
232
  - `generateSpecialFiles`
233
+ - `generateFeed`
232
234
  - `generateRobotsTxt`
233
235
  - `writeManifest`
234
236
 
@@ -238,6 +240,7 @@ Defaults:
238
240
 
239
241
  - `assetHashing: true`
240
242
  - `generateSpecialFiles: true`
243
+ - `generateFeed: true`
241
244
  - `generateRobotsTxt: true`
242
245
  - `writeManifest: false`
243
246
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeropress/build-core",
3
- "version": "0.6.5",
3
+ "version": "0.6.8",
4
4
  "description": "Shared ZeroPress build core",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@zeropress/slug-policy": "0.1.0",
37
- "@zeropress/preview-data-validator": "0.6.3",
37
+ "@zeropress/preview-data-validator": "0.6.6",
38
38
  "@zeropress/theme-validator": "0.6.5",
39
39
  "highlight.js": "11.11.1",
40
40
  "markdown-it": "14.2.0",
@@ -9,6 +9,7 @@ import { ZeroPressEngine } from '../render/zeropress-engine.js';
9
9
  const DEFAULT_OPTIONS = {
10
10
  assetHashing: true,
11
11
  generateSpecialFiles: true,
12
+ generateFeed: true,
12
13
  generateRobotsTxt: true,
13
14
  writeManifest: false,
14
15
  };
@@ -115,7 +116,9 @@ export async function buildSite(input) {
115
116
  buildSitemapXml(state.previewData.site, state.emitted, state.generatedAt, options.sitemapStylesheetHref),
116
117
  'application/xml',
117
118
  );
118
- await writeOutput(state.writer, state.summaries, 'feed.xml', buildFeedXml(state.previewData.site, state.emitted, state.generatedAt), 'application/rss+xml');
119
+ if (shouldGenerateFeed(options)) {
120
+ await writeOutput(state.writer, state.summaries, 'feed.xml', buildFeedXml(state.previewData.site, state.emitted, state.generatedAt), 'application/rss+xml');
121
+ }
119
122
  }
120
123
  if (shouldGenerateRobotsTxt(options)) {
121
124
  await writeOutput(state.writer, state.summaries, 'robots.txt', buildRobotsTxt(state.previewData.site), 'text/plain');
@@ -294,6 +297,7 @@ async function renderFrontPage(state, route) {
294
297
  url: currentUrl,
295
298
  title: page.title,
296
299
  description: page.excerpt,
300
+ updatedAt: page.updated_at_iso,
297
301
  includeInFeed: false,
298
302
  includeInSitemap: !isDelistedDocument(page),
299
303
  };
@@ -400,6 +404,7 @@ async function renderPage(state, page) {
400
404
  url: currentUrl,
401
405
  title: page.title,
402
406
  description: page.excerpt,
407
+ updatedAt: page.updated_at_iso,
403
408
  status: page.status,
404
409
  includeInSitemap: page.omit_from_sitemap !== true && !isDelistedDocument(page),
405
410
  });
@@ -508,6 +513,7 @@ function normalizePreviewData(previewData, options = {}) {
508
513
  const featuredMedia = deriveManagedMedia(featuredImage, mediaRegistry, normalizedSite);
509
514
  return {
510
515
  ...page,
516
+ ...(page.updated_at_iso ? { updated_at_iso: normalizeIsoTimestamp(page.updated_at_iso) } : {}),
511
517
  discoverability: normalizeDiscoverability(page.discoverability),
512
518
  featured_image: featuredImage,
513
519
  ...(featuredMedia ? { featured_media: featuredMedia } : {}),
@@ -995,6 +1001,8 @@ function buildCollectionPageSummary(page, frontPage) {
995
1001
  url: frontPage?.type === 'page' && frontPage.page_slug === page.slug ? '/' : page.url,
996
1002
  excerpt: page.excerpt || '',
997
1003
  featured_image: page.featured_image || '',
1004
+ updated_at: page.updated_at || '',
1005
+ updated_at_iso: page.updated_at_iso || '',
998
1006
  ...(page.featured_media ? { featured_media: { ...page.featured_media } } : {}),
999
1007
  meta: page.meta,
1000
1008
  data: page.data,
@@ -1057,6 +1065,8 @@ function buildCollectionCursorItemSummary(item) {
1057
1065
  url: item.url,
1058
1066
  excerpt: item.excerpt || '',
1059
1067
  featured_image: item.featured_image || '',
1068
+ updated_at: item.updated_at || '',
1069
+ updated_at_iso: item.updated_at_iso || '',
1060
1070
  meta: item.meta,
1061
1071
  data: item.data,
1062
1072
  };
@@ -1389,6 +1399,7 @@ function preparePage(page, site) {
1389
1399
  document_type: documentType,
1390
1400
  html: renderedDocument.html,
1391
1401
  toc: renderedDocument.toc,
1402
+ updated_at: page.updated_at_iso ? formatTimestamp(page.updated_at_iso, site) : '',
1392
1403
  };
1393
1404
  }
1394
1405
 
@@ -2421,7 +2432,10 @@ function assertPlannedOutputPathsSafe(state) {
2421
2432
  plannedPaths.push('robots.txt');
2422
2433
  }
2423
2434
  if (hasCanonicalSiteUrl(state.previewData.site.url)) {
2424
- plannedPaths.push('sitemap.xml', 'feed.xml');
2435
+ plannedPaths.push('sitemap.xml');
2436
+ if (shouldGenerateFeed(state.options)) {
2437
+ plannedPaths.push('feed.xml');
2438
+ }
2425
2439
  }
2426
2440
  }
2427
2441
 
@@ -2633,7 +2647,7 @@ function buildSearchPageItem(page, url) {
2633
2647
  categories: [],
2634
2648
  tags: [],
2635
2649
  published_at_iso: '',
2636
- updated_at_iso: '',
2650
+ updated_at_iso: normalizeIsoTimestamp(page.updated_at_iso),
2637
2651
  content_text: htmlToSearchText(page.html),
2638
2652
  };
2639
2653
  }
@@ -3073,6 +3087,7 @@ function buildSitemapXml(site, emitted, generatedAt, stylesheetHref = '') {
3073
3087
  ...(emitted.frontPage && emitted.frontPage.includeInSitemap !== false
3074
3088
  ? [{
3075
3089
  url: emitted.frontPage.url,
3090
+ ...(emitted.frontPage.updatedAt ? { lastmod: toDate(emitted.frontPage.updatedAt) } : {}),
3076
3091
  changefreq: 'daily',
3077
3092
  priority: 1.0,
3078
3093
  }]
@@ -3094,6 +3109,7 @@ function buildSitemapXml(site, emitted, generatedAt, stylesheetHref = '') {
3094
3109
  .filter((page) => page.includeInSitemap !== false)
3095
3110
  .map((page) => ({
3096
3111
  url: page.url,
3112
+ ...(page.updatedAt ? { lastmod: toDate(page.updatedAt) } : {}),
3097
3113
  changefreq: 'monthly',
3098
3114
  priority: 0.7,
3099
3115
  })),
@@ -3148,6 +3164,10 @@ function shouldGenerateRobotsTxt(options) {
3148
3164
  return options.generateSpecialFiles && options.generateRobotsTxt !== false;
3149
3165
  }
3150
3166
 
3167
+ function shouldGenerateFeed(options) {
3168
+ return options.generateSpecialFiles && options.generateFeed !== false;
3169
+ }
3170
+
3151
3171
  function shouldGenerateSearchArtifacts(state, options) {
3152
3172
  return options.generateSpecialFiles && state.previewData.site.search === true;
3153
3173
  }
package/src/index.d.ts CHANGED
@@ -30,6 +30,7 @@ export interface BuildOptions {
30
30
  };
31
31
  sitemapStylesheetHref?: string;
32
32
  generateSpecialFiles?: boolean;
33
+ generateFeed?: boolean;
33
34
  generateRobotsTxt?: boolean;
34
35
  writeManifest?: boolean;
35
36
  }
@@ -49,7 +49,7 @@ function renderMarkdownDocument(content) {
49
49
  function createMarkdownRenderer(toc) {
50
50
  const markdown = new MarkdownIt({
51
51
  html: true,
52
- linkify: true,
52
+ linkify: false,
53
53
  typographer: true,
54
54
  breaks: true,
55
55
  highlight(value, language) {
@@ -162,8 +162,10 @@ function markdownTaskLists(markdown) {
162
162
  addTokenClass(listItemToken, 'task-list-item');
163
163
 
164
164
  const checkboxToken = new state.Token('html_inline', '', 0);
165
- const checked = markerMatch[1].toLowerCase() === 'x' ? ' checked' : '';
166
- checkboxToken.content = `<input class="task-list-item-checkbox" type="checkbox"${checked} disabled> `;
165
+ const isChecked = markerMatch[1].toLowerCase() === 'x';
166
+ const checked = isChecked ? ' checked' : '';
167
+ const label = isChecked ? 'Completed task' : 'Incomplete task';
168
+ checkboxToken.content = `<input class="task-list-item-checkbox" type="checkbox"${checked} disabled aria-label="${label}"> `;
167
169
 
168
170
  const strippedContent = token.content.slice(markerMatch[0].length);
169
171
  token.content = strippedContent;
@@ -337,7 +339,7 @@ function sanitizeHtml(html) {
337
339
  aside: new Set(['role', 'class', 'id']),
338
340
  img: new Set(['src', 'srcset', 'sizes', 'alt', 'title', 'class', 'id', 'width', 'height', 'loading', 'decoding']),
339
341
  iframe: new Set(['src', 'width', 'height', 'frameborder', 'allowfullscreen', 'class']),
340
- input: new Set(['type', 'checked', 'disabled', 'class', 'id']),
342
+ input: new Set(['type', 'checked', 'disabled', 'class', 'id', 'aria-label']),
341
343
  source: new Set(['src', 'srcset', 'sizes', 'type', 'media', 'width', 'height', 'class', 'id']),
342
344
  '*': new Set(['class', 'id']),
343
345
  };