@zeropress/build-pages 0.5.4 → 0.5.6

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/dist/action.js CHANGED
@@ -52248,6 +52248,9 @@ var PREVIEW_MENU_ITEM_TYPES = ["custom", "page", "post", "category"];
52248
52248
  var PREVIEW_MENU_TARGETS = ["_self", "_blank"];
52249
52249
  var PREVIEW_MENU_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
52250
52250
  var PREVIEW_WIDGET_AREA_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
52251
+ var PREVIEW_COLLECTION_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
52252
+ var PREVIEW_COLLECTION_ITEM_TYPES = ["post", "page"];
52253
+ var PREVIEW_MEDIA_DELIVERY_MODES = ["none", "media_domain"];
52251
52254
  var PREVIEW_PERMALINK_OUTPUT_STYLES = ["directory", "html-extension"];
52252
52255
  var PREVIEW_PERMALINK_FIELDS = ["posts", "pages", "categories", "tags"];
52253
52256
  var PREVIEW_FRONT_PAGE_TYPES = ["theme_index", "page", "standalone_html"];
@@ -52259,7 +52262,7 @@ var PREVIEW_PERMALINK_TOKENS = Object.freeze({
52259
52262
  });
52260
52263
  function validatePreviewData(data) {
52261
52264
  const errors = [];
52262
- validateClosedObject(data, "", errors, ["$schema", "version", "generator", "generated_at", "site", "content", "menus", "widgets", "custom_css", "custom_html"]);
52265
+ validateClosedObject(data, "", errors, ["$schema", "version", "generator", "generated_at", "site", "content", "menus", "widgets", "collections", "custom_css", "custom_html"]);
52263
52266
  if (isObject(data)) {
52264
52267
  if (data.$schema !== void 0) {
52265
52268
  validateString(data.$schema, "$schema", "INVALID_SCHEMA_HINT", errors);
@@ -52269,8 +52272,15 @@ function validatePreviewData(data) {
52269
52272
  validateDateTimeString(data.generated_at, "generated_at", "INVALID_GENERATED_AT", errors);
52270
52273
  validateSite(data.site, "site", errors);
52271
52274
  validateContent(data.content, "content", errors);
52272
- validateMenus(data.menus, "menus", errors);
52273
- validateWidgets(data.widgets, "widgets", errors);
52275
+ if (data.menus !== void 0) {
52276
+ validateMenus(data.menus, "menus", errors);
52277
+ }
52278
+ if (data.widgets !== void 0) {
52279
+ validateWidgets(data.widgets, "widgets", errors);
52280
+ }
52281
+ if (data.collections !== void 0) {
52282
+ validateCollections(data.collections, "collections", errors);
52283
+ }
52274
52284
  if (data.custom_css !== void 0) {
52275
52285
  validateCustomCss(data.custom_css, "custom_css", errors);
52276
52286
  }
@@ -52293,7 +52303,26 @@ function assertPreviewData(data) {
52293
52303
  return data;
52294
52304
  }
52295
52305
  function validateSite(site, path4, errors) {
52296
- validateObject(site, path4, "INVALID_SITE", errors);
52306
+ validateClosedObject(site, path4, errors, [
52307
+ "title",
52308
+ "description",
52309
+ "url",
52310
+ "mediaBaseUrl",
52311
+ "mediaDeliveryMode",
52312
+ "favicon",
52313
+ "locale",
52314
+ "postsPerPage",
52315
+ "dateFormat",
52316
+ "timeFormat",
52317
+ "timezone",
52318
+ "disallowComments",
52319
+ "indexing",
52320
+ "permalinks",
52321
+ "front_page",
52322
+ "post_index",
52323
+ "footer",
52324
+ "meta"
52325
+ ]);
52297
52326
  if (!isObject(site)) {
52298
52327
  return;
52299
52328
  }
@@ -52301,15 +52330,25 @@ function validateSite(site, path4, errors) {
52301
52330
  validateString(site.description, `${path4}.description`, "INVALID_SITE_DESCRIPTION", errors);
52302
52331
  validateSiteUri(site.url, `${path4}.url`, "INVALID_SITE_URL", errors);
52303
52332
  validateSiteUri(site.mediaBaseUrl, `${path4}.mediaBaseUrl`, "INVALID_SITE_MEDIA_BASE_URL", errors);
52333
+ if (site.mediaDeliveryMode !== void 0) {
52334
+ validateEnum(site.mediaDeliveryMode, `${path4}.mediaDeliveryMode`, "INVALID_SITE_MEDIA_DELIVERY_MODE", errors, PREVIEW_MEDIA_DELIVERY_MODES);
52335
+ }
52336
+ if (site.favicon !== void 0) {
52337
+ validateSiteFavicon(site.favicon, `${path4}.favicon`, errors);
52338
+ }
52304
52339
  validateNonEmptyString(site.locale, `${path4}.locale`, "INVALID_SITE_LOCALE", errors);
52305
52340
  validateInteger(site.postsPerPage, `${path4}.postsPerPage`, "INVALID_SITE_POSTS_PER_PAGE", errors, { minimum: 1 });
52306
52341
  validateNonEmptyString(site.dateFormat, `${path4}.dateFormat`, "INVALID_SITE_DATE_FORMAT", errors);
52307
52342
  validateString(site.timeFormat, `${path4}.timeFormat`, "INVALID_SITE_TIME_FORMAT", errors);
52308
52343
  validateNonEmptyString(site.timezone, `${path4}.timezone`, "INVALID_SITE_TIMEZONE", errors);
52309
52344
  validateBoolean(site.disallowComments, `${path4}.disallowComments`, "INVALID_SITE_DISALLOW_COMMENTS", errors);
52345
+ if (site.indexing !== void 0) {
52346
+ validateBoolean(site.indexing, `${path4}.indexing`, "INVALID_SITE_INDEXING", errors);
52347
+ }
52310
52348
  validatePermalinks(site.permalinks, `${path4}.permalinks`, errors);
52311
52349
  validateFrontPage(site.front_page, `${path4}.front_page`, errors);
52312
52350
  validatePostIndex(site.post_index, `${path4}.post_index`, errors);
52351
+ validatePreviewMeta(site.meta, `${path4}.meta`, errors);
52313
52352
  if (site.footer !== void 0) {
52314
52353
  validateSiteFooter(site.footer, `${path4}.footer`, errors);
52315
52354
  }
@@ -52328,7 +52367,7 @@ function validateSite(site, path4, errors) {
52328
52367
  ], "INVALID_LEGACY_SITE_FIELD");
52329
52368
  }
52330
52369
  function validateContent(content, path4, errors) {
52331
- validateClosedObject(content, path4, errors, ["authors", "posts", "pages", "categories", "tags"]);
52370
+ validateClosedObject(content, path4, errors, ["authors", "posts", "pages", "categories", "tags", "media"]);
52332
52371
  if (!isObject(content)) {
52333
52372
  return;
52334
52373
  }
@@ -52343,6 +52382,9 @@ function validateContent(content, path4, errors) {
52343
52382
  validateArray(content.tags, `${path4}.tags`, "INVALID_TAGS", errors, (entry, index) => {
52344
52383
  validatePreviewTag(entry, `${path4}.tags[${index}]`, errors);
52345
52384
  });
52385
+ if (content.media !== void 0) {
52386
+ validateMediaArray(content.media, `${path4}.media`, errors);
52387
+ }
52346
52388
  }
52347
52389
  function validateSiteFooter(footer, path4, errors) {
52348
52390
  validateClosedObject(footer, path4, errors, ["copyright_text", "attribution"]);
@@ -52407,6 +52449,49 @@ function validateWidgets(widgets, path4, errors) {
52407
52449
  validatePreviewWidgetArea(widgetArea, `${path4}.${widgetAreaId}`, errors);
52408
52450
  }
52409
52451
  }
52452
+ function validateCollections(collections, path4, errors) {
52453
+ validateObject(collections, path4, "INVALID_COLLECTIONS", errors);
52454
+ if (!isObject(collections)) {
52455
+ return;
52456
+ }
52457
+ for (const [collectionId, collection] of Object.entries(collections)) {
52458
+ if (!PREVIEW_COLLECTION_ID_PATTERN.test(collectionId)) {
52459
+ errors.push(issue("INVALID_COLLECTION_ID", `${path4}.${collectionId}`, "Collection ids must match ^[a-z][a-z0-9_-]{0,63}$"));
52460
+ }
52461
+ validatePreviewCollection(collection, `${path4}.${collectionId}`, errors);
52462
+ }
52463
+ }
52464
+ function validatePreviewCollection(collection, path4, errors) {
52465
+ validateClosedObject(collection, path4, errors, ["title", "description", "items"]);
52466
+ if (!isObject(collection)) {
52467
+ return;
52468
+ }
52469
+ if (collection.title !== void 0) {
52470
+ validateNonEmptyString(collection.title, `${path4}.title`, "INVALID_COLLECTION_TITLE", errors);
52471
+ }
52472
+ if (collection.description !== void 0) {
52473
+ validateString(collection.description, `${path4}.description`, "INVALID_COLLECTION_DESCRIPTION", errors);
52474
+ }
52475
+ const seenItems = /* @__PURE__ */ new Set();
52476
+ validateArray(collection.items, `${path4}.items`, "INVALID_COLLECTION_ITEMS", errors, (entry, index) => {
52477
+ validatePreviewCollectionItem(entry, `${path4}.items[${index}]`, errors, seenItems);
52478
+ });
52479
+ }
52480
+ function validatePreviewCollectionItem(item, path4, errors, seenItems) {
52481
+ validateClosedObject(item, path4, errors, ["type", "slug"]);
52482
+ if (!isObject(item)) {
52483
+ return;
52484
+ }
52485
+ validateEnum(item.type, `${path4}.type`, "INVALID_COLLECTION_ITEM_TYPE", errors, PREVIEW_COLLECTION_ITEM_TYPES);
52486
+ validateSlugSegment2(item.slug, `${path4}.slug`, "INVALID_COLLECTION_ITEM_SLUG", errors);
52487
+ if (typeof item.type === "string" && PREVIEW_COLLECTION_ITEM_TYPES.includes(item.type) && typeof item.slug === "string") {
52488
+ const key = `${item.type}:${item.slug}`;
52489
+ if (seenItems.has(key)) {
52490
+ errors.push(issue("DUPLICATE_COLLECTION_ITEM", `${path4}.slug`, "Duplicate collection item in the same collection"));
52491
+ }
52492
+ seenItems.add(key);
52493
+ }
52494
+ }
52410
52495
  function validatePreviewWidgetArea(widgetArea, path4, errors) {
52411
52496
  validateClosedObject(widgetArea, path4, errors, ["name", "items"]);
52412
52497
  if (!isObject(widgetArea)) {
@@ -52498,6 +52583,46 @@ function validatePreviewAuthor(author, path4, errors) {
52498
52583
  validateUrlLike(author.avatar, `${path4}.avatar`, "INVALID_AUTHOR_AVATAR", errors);
52499
52584
  }
52500
52585
  }
52586
+ function validateMediaArray(value, path4, errors) {
52587
+ const sources = /* @__PURE__ */ new Set();
52588
+ validateArray(value, path4, "INVALID_MEDIA", errors, (entry, index) => {
52589
+ validatePreviewMedia(entry, `${path4}[${index}]`, errors);
52590
+ if (!isObject(entry) || typeof entry.src !== "string") {
52591
+ return;
52592
+ }
52593
+ if (sources.has(entry.src)) {
52594
+ errors.push(issue("DUPLICATE_MEDIA_SRC", `${path4}[${index}].src`, "Media src values must be unique"));
52595
+ return;
52596
+ }
52597
+ sources.add(entry.src);
52598
+ });
52599
+ }
52600
+ function validateSiteFavicon(favicon, path4, errors) {
52601
+ validateClosedObject(favicon, path4, errors, ["icon", "svg", "png", "apple_touch_icon"]);
52602
+ if (!isObject(favicon)) {
52603
+ return;
52604
+ }
52605
+ if (favicon.icon === void 0 && favicon.svg === void 0 && favicon.png === void 0 && favicon.apple_touch_icon === void 0) {
52606
+ errors.push(issue("INVALID_SITE_FAVICON", path4, "site.favicon must include at least one favicon URL"));
52607
+ }
52608
+ for (const key of ["icon", "svg", "png", "apple_touch_icon"]) {
52609
+ if (favicon[key] !== void 0) {
52610
+ validateUrlLike(favicon[key], `${path4}.${key}`, "INVALID_SITE_FAVICON_URL", errors);
52611
+ }
52612
+ }
52613
+ }
52614
+ function validatePreviewMedia(media, path4, errors) {
52615
+ validateClosedObject(media, path4, errors, ["src", "width", "height", "alt"]);
52616
+ if (!isObject(media)) {
52617
+ return;
52618
+ }
52619
+ validateUrlLike(media.src, `${path4}.src`, "INVALID_MEDIA_SRC", errors);
52620
+ validateInteger(media.width, `${path4}.width`, "INVALID_MEDIA_WIDTH", errors, { minimum: 1 });
52621
+ validateInteger(media.height, `${path4}.height`, "INVALID_MEDIA_HEIGHT", errors, { minimum: 1 });
52622
+ if (media.alt !== void 0) {
52623
+ validateString(media.alt, `${path4}.alt`, "INVALID_MEDIA_ALT", errors);
52624
+ }
52625
+ }
52501
52626
  function validatePreviewPost(post, path4, errors, authorIds) {
52502
52627
  validateClosedObject(post, path4, errors, [
52503
52628
  "id",
@@ -52787,7 +52912,7 @@ function validateClosedObject(value, path4, errors, allowedKeys) {
52787
52912
  return;
52788
52913
  }
52789
52914
  for (const key of Object.keys(value)) {
52790
- if (!allowedKeys.includes(key) && !isSiteExtensionKey(path4, key)) {
52915
+ if (!allowedKeys.includes(key)) {
52791
52916
  errors.push(issue("UNKNOWN_PROPERTY", path4 ? `${path4}.${key}` : key, "Unexpected property"));
52792
52917
  }
52793
52918
  }
@@ -52799,13 +52924,16 @@ function validateClosedObject(value, path4, errors, allowedKeys) {
52799
52924
  }
52800
52925
  function isOptionalKey(path4, key) {
52801
52926
  if (path4 === "") {
52802
- return key === "$schema" || key === "custom_css" || key === "custom_html";
52927
+ return key === "$schema" || key === "menus" || key === "widgets" || key === "collections" || key === "custom_css" || key === "custom_html";
52803
52928
  }
52804
52929
  if (path4 === "custom_html") {
52805
52930
  return key === "head_end" || key === "body_end";
52806
52931
  }
52807
52932
  if (path4 === "site") {
52808
- return key === "permalinks" || key === "front_page" || key === "post_index" || key === "footer";
52933
+ return key === "mediaDeliveryMode" || key === "favicon" || key === "indexing" || key === "permalinks" || key === "front_page" || key === "post_index" || key === "footer" || key === "meta";
52934
+ }
52935
+ if (path4 === "site.favicon") {
52936
+ return key === "icon" || key === "svg" || key === "png" || key === "apple_touch_icon";
52809
52937
  }
52810
52938
  if (path4 === "site.footer") {
52811
52939
  return key === "copyright_text" || key === "attribution";
@@ -52822,12 +52950,21 @@ function isOptionalKey(path4, key) {
52822
52950
  if (path4 === "site.permalinks") {
52823
52951
  return key === "output_style" || PREVIEW_PERMALINK_FIELDS.includes(key);
52824
52952
  }
52953
+ if (path4 === "content") {
52954
+ return key === "media";
52955
+ }
52825
52956
  if (path4.startsWith("content.authors[")) {
52826
52957
  return key === "avatar";
52827
52958
  }
52959
+ if (path4.startsWith("content.media[")) {
52960
+ return key === "alt";
52961
+ }
52828
52962
  if (path4.startsWith("widgets.") && path4.includes(".items[")) {
52829
52963
  return key === "settings";
52830
52964
  }
52965
+ if (path4.startsWith("collections.") && !path4.includes(".items[")) {
52966
+ return key === "title" || key === "description";
52967
+ }
52831
52968
  if (path4.startsWith("content.posts[")) {
52832
52969
  return key === "id" || key === "featured_image" || key === "meta";
52833
52970
  }
@@ -52839,21 +52976,6 @@ function isOptionalKey(path4, key) {
52839
52976
  }
52840
52977
  return false;
52841
52978
  }
52842
- function isSiteExtensionKey(path4, key) {
52843
- if (path4 !== "site") {
52844
- return false;
52845
- }
52846
- return ![
52847
- "site_name",
52848
- "site_description",
52849
- "site_url",
52850
- "metadata",
52851
- "media_delivery_mode",
52852
- "media_delivery_base_url",
52853
- "site_timezone",
52854
- "site_locale"
52855
- ].includes(key);
52856
- }
52857
52979
  function validateSlugArray(value, path4, code2, errors) {
52858
52980
  if (!Array.isArray(value)) {
52859
52981
  errors.push(issue(code2, path4, "Expected an array"));
@@ -53031,6 +53153,27 @@ var MENU_SLOT_COUNT_MAX = 12;
53031
53153
  var MENU_SLOT_TITLE_MAX_LENGTH = 80;
53032
53154
  var MENU_SLOT_DESCRIPTION_MAX_LENGTH = 160;
53033
53155
  var SUPPORTED_THEME_FEATURES = /* @__PURE__ */ new Set(["comments", "newsletter", "postIndex"]);
53156
+ var THEME_MANIFEST_KEYS = /* @__PURE__ */ new Set([
53157
+ "$schema",
53158
+ "name",
53159
+ "namespace",
53160
+ "slug",
53161
+ "version",
53162
+ "license",
53163
+ "runtime",
53164
+ "author",
53165
+ "description",
53166
+ "thumbnail",
53167
+ "features",
53168
+ "menuSlots",
53169
+ "widgetAreas",
53170
+ "siteMeta",
53171
+ "collectionSlots"
53172
+ ]);
53173
+ var SITE_META_KEY_REGEX = /^[a-z][a-z0-9_]*(?:-[a-z0-9_]+)*$/;
53174
+ var SITE_META_KEY_MAX_LENGTH = 64;
53175
+ var SITE_META_COUNT_MAX = 32;
53176
+ var SITE_META_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean"]);
53034
53177
  function validateFeatureFlags(rawValue, errors) {
53035
53178
  if (rawValue === void 0) {
53036
53179
  return void 0;
@@ -53161,6 +53304,127 @@ function validateHelperMetadataMap(rawValue, fieldName, issueCodes, errors) {
53161
53304
  }
53162
53305
  return Object.keys(normalizedItems).length > 0 ? normalizedItems : void 0;
53163
53306
  }
53307
+ function validateSiteMetaMap(rawValue, errors) {
53308
+ if (rawValue === void 0) {
53309
+ return void 0;
53310
+ }
53311
+ if (!rawValue || typeof rawValue !== "object" || Array.isArray(rawValue)) {
53312
+ errors.push(issue2(
53313
+ "INVALID_SITE_META",
53314
+ "theme.json",
53315
+ "theme.json field 'siteMeta' must be an object when present",
53316
+ "error"
53317
+ ));
53318
+ return void 0;
53319
+ }
53320
+ const entries = Object.entries(rawValue);
53321
+ if (entries.length === 0) {
53322
+ errors.push(issue2(
53323
+ "INVALID_SITE_META",
53324
+ "theme.json",
53325
+ "theme.json field 'siteMeta' must not be empty",
53326
+ "error"
53327
+ ));
53328
+ }
53329
+ if (entries.length > SITE_META_COUNT_MAX) {
53330
+ errors.push(issue2(
53331
+ "INVALID_SITE_META",
53332
+ "theme.json",
53333
+ `theme.json field 'siteMeta' must contain at most ${SITE_META_COUNT_MAX} keys`,
53334
+ "error"
53335
+ ));
53336
+ }
53337
+ const normalizedItems = {};
53338
+ for (const [metaKey, value] of entries) {
53339
+ if (!SITE_META_KEY_REGEX.test(metaKey) || metaKey.length > SITE_META_KEY_MAX_LENGTH) {
53340
+ errors.push(issue2(
53341
+ "INVALID_SITE_META_KEY",
53342
+ `theme.json.siteMeta.${metaKey}`,
53343
+ `siteMeta key '${metaKey}' must start with a lowercase letter and use lowercase letters, digits, underscores, or internal hyphens only`,
53344
+ "error"
53345
+ ));
53346
+ continue;
53347
+ }
53348
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
53349
+ errors.push(issue2(
53350
+ "INVALID_SITE_META_FIELD",
53351
+ `theme.json.siteMeta.${metaKey}`,
53352
+ `siteMeta key '${metaKey}' must be an object`,
53353
+ "error"
53354
+ ));
53355
+ continue;
53356
+ }
53357
+ const allowedKeys = /* @__PURE__ */ new Set(["title", "description", "type", "default"]);
53358
+ for (const key of Object.keys(value)) {
53359
+ if (!allowedKeys.has(key)) {
53360
+ errors.push(issue2(
53361
+ "INVALID_SITE_META_PROPERTY",
53362
+ `theme.json.siteMeta.${metaKey}.${key}`,
53363
+ `Unknown siteMeta property '${key}' in key '${metaKey}'`,
53364
+ "error"
53365
+ ));
53366
+ }
53367
+ }
53368
+ if (typeof value.title !== "string" || value.title.trim() === "") {
53369
+ errors.push(issue2(
53370
+ "INVALID_SITE_META_TITLE",
53371
+ `theme.json.siteMeta.${metaKey}.title`,
53372
+ `siteMeta key '${metaKey}' must define a non-empty 'title'`,
53373
+ "error"
53374
+ ));
53375
+ } else if (value.title.trim().length > MENU_SLOT_TITLE_MAX_LENGTH) {
53376
+ errors.push(issue2(
53377
+ "INVALID_SITE_META_TITLE",
53378
+ `theme.json.siteMeta.${metaKey}.title`,
53379
+ `siteMeta key '${metaKey}' title must be at most ${MENU_SLOT_TITLE_MAX_LENGTH} characters`,
53380
+ "error"
53381
+ ));
53382
+ }
53383
+ if (value.description !== void 0 && (typeof value.description !== "string" || value.description.trim().length > MENU_SLOT_DESCRIPTION_MAX_LENGTH)) {
53384
+ errors.push(issue2(
53385
+ "INVALID_SITE_META_DESCRIPTION",
53386
+ `theme.json.siteMeta.${metaKey}.description`,
53387
+ `siteMeta key '${metaKey}' description must be a string at most ${MENU_SLOT_DESCRIPTION_MAX_LENGTH} characters`,
53388
+ "error"
53389
+ ));
53390
+ }
53391
+ if (value.type !== void 0 && !SITE_META_TYPES.has(value.type)) {
53392
+ errors.push(issue2(
53393
+ "INVALID_SITE_META_TYPE",
53394
+ `theme.json.siteMeta.${metaKey}.type`,
53395
+ `siteMeta key '${metaKey}' type must be one of: string, number, boolean`,
53396
+ "error"
53397
+ ));
53398
+ }
53399
+ if (value.default !== void 0) {
53400
+ const defaultType = typeof value.default;
53401
+ if (value.default !== null && defaultType !== "string" && defaultType !== "number" && defaultType !== "boolean") {
53402
+ errors.push(issue2(
53403
+ "INVALID_SITE_META_DEFAULT",
53404
+ `theme.json.siteMeta.${metaKey}.default`,
53405
+ `siteMeta key '${metaKey}' default must be a string, number, boolean, or null`,
53406
+ "error"
53407
+ ));
53408
+ } else if (typeof value.type === "string" && SITE_META_TYPES.has(value.type) && value.default !== null && defaultType !== value.type) {
53409
+ errors.push(issue2(
53410
+ "INVALID_SITE_META_DEFAULT",
53411
+ `theme.json.siteMeta.${metaKey}.default`,
53412
+ `siteMeta key '${metaKey}' default must match its declared type`,
53413
+ "error"
53414
+ ));
53415
+ }
53416
+ }
53417
+ if (typeof value.title === "string" && value.title.trim() !== "" && value.title.trim().length <= MENU_SLOT_TITLE_MAX_LENGTH && (value.description === void 0 || typeof value.description === "string" && value.description.trim().length <= MENU_SLOT_DESCRIPTION_MAX_LENGTH) && (value.type === void 0 || SITE_META_TYPES.has(value.type)) && (value.default === void 0 || value.default === null || typeof value.default === "string" || typeof value.default === "number" || typeof value.default === "boolean")) {
53418
+ normalizedItems[metaKey] = {
53419
+ title: value.title.trim(),
53420
+ ...typeof value.description === "string" && value.description.trim() !== "" ? { description: value.description.trim() } : {},
53421
+ ...typeof value.type === "string" && SITE_META_TYPES.has(value.type) ? { type: value.type } : {},
53422
+ ...value.default !== void 0 ? { default: value.default } : {}
53423
+ };
53424
+ }
53425
+ }
53426
+ return Object.keys(normalizedItems).length > 0 ? normalizedItems : void 0;
53427
+ }
53164
53428
  async function validateThemeFiles(fileMap, options2 = {}) {
53165
53429
  const files = normalizeFileMap(fileMap);
53166
53430
  const errors = [];
@@ -53254,6 +53518,16 @@ function validateManifest(themeJson) {
53254
53518
  license: "",
53255
53519
  runtime: ""
53256
53520
  };
53521
+ for (const key of Object.keys(themeJson)) {
53522
+ if (!THEME_MANIFEST_KEYS.has(key)) {
53523
+ errors.push(issue2(
53524
+ "UNKNOWN_THEME_MANIFEST_FIELD",
53525
+ `theme.json.${key}`,
53526
+ `Unknown theme.json field '${key}'`,
53527
+ "error"
53528
+ ));
53529
+ }
53530
+ }
53257
53531
  if (themeJson.$schema !== void 0 && typeof themeJson.$schema !== "string") {
53258
53532
  errors.push(issue2(
53259
53533
  "INVALID_SCHEMA_HINT",
@@ -53339,6 +53613,18 @@ function validateManifest(themeJson) {
53339
53613
  manifest.description = description;
53340
53614
  }
53341
53615
  }
53616
+ if (themeJson.thumbnail !== void 0) {
53617
+ if (typeof themeJson.thumbnail !== "string") {
53618
+ errors.push(issue2(
53619
+ "INVALID_THUMBNAIL",
53620
+ "theme.json.thumbnail",
53621
+ "theme.json field 'thumbnail' must be a string when present",
53622
+ "error"
53623
+ ));
53624
+ } else {
53625
+ manifest.thumbnail = themeJson.thumbnail;
53626
+ }
53627
+ }
53342
53628
  const features = validateFeatureFlags(themeJson.features, errors);
53343
53629
  if (features) {
53344
53630
  manifest.features = features;
@@ -53371,6 +53657,24 @@ function validateManifest(themeJson) {
53371
53657
  if (widgetAreas) {
53372
53658
  manifest.widgetAreas = widgetAreas;
53373
53659
  }
53660
+ const siteMeta = validateSiteMetaMap(themeJson.siteMeta, errors);
53661
+ if (siteMeta) {
53662
+ manifest.siteMeta = siteMeta;
53663
+ }
53664
+ const collectionSlots = validateHelperMetadataMap(themeJson.collectionSlots, "collectionSlots", {
53665
+ itemLabel: "Collection slot",
53666
+ propertyLabel: "collection slot property",
53667
+ collectionLabel: "slots",
53668
+ invalidCollectionCode: "INVALID_COLLECTION_SLOTS",
53669
+ invalidIdCode: "INVALID_COLLECTION_SLOT_ID",
53670
+ invalidItemCode: "INVALID_COLLECTION_SLOT",
53671
+ invalidPropertyCode: "INVALID_COLLECTION_SLOT_PROPERTY",
53672
+ invalidTitleCode: "INVALID_COLLECTION_SLOT_TITLE",
53673
+ invalidDescriptionCode: "INVALID_COLLECTION_SLOT_DESCRIPTION"
53674
+ }, errors);
53675
+ if (collectionSlots) {
53676
+ manifest.collectionSlots = collectionSlots;
53677
+ }
53374
53678
  return { errors, manifest: errors.length > 0 ? void 0 : manifest };
53375
53679
  }
53376
53680
  function validateTemplateSyntax(templatePath, content, context) {
@@ -53814,7 +54118,7 @@ var AssetProcessor = class {
53814
54118
  return css.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/\s*([{}:;,>+~])\s*/g, "$1").replace(/;}/g, "}").trim();
53815
54119
  }
53816
54120
  async processJavaScript(js) {
53817
- return js.replace(/\/\/(?![^\r\n]*https?:)[^\r\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s+/g, " ").replace(/\s*([{}();,=+\-*/<>!&|])\s*/g, "$1").trim();
54121
+ return js;
53818
54122
  }
53819
54123
  generateAssetHash(content) {
53820
54124
  const hash = createHash("sha256");
@@ -59902,6 +60206,7 @@ var ZeroPressEngine = class {
59902
60206
  var DEFAULT_OPTIONS = {
59903
60207
  assetHashing: true,
59904
60208
  generateSpecialFiles: true,
60209
+ generateRobotsTxt: true,
59905
60210
  writeManifest: false
59906
60211
  };
59907
60212
  var DEFAULT_POSTS_PER_PAGE = 10;
@@ -59929,6 +60234,9 @@ var COMMENT_POLICY_OUTPUT_PATH = "_zeropress/comment-policy.json";
59929
60234
  var OUTPUT_PATH_CONTROL_CHAR_PATTERN = /[\u0000-\u001F\u007F]/;
59930
60235
  var SAFE_MEDIA_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
59931
60236
  var SAFE_LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
60237
+ var MEDIA_DELIVERY_MODES = /* @__PURE__ */ new Set(["none", "media_domain"]);
60238
+ var RESPONSIVE_IMAGE_WIDTHS = [320, 480, 768, 1024, 1280, 1600, 1920];
60239
+ var RESPONSIVE_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["jpg", "jpeg", "png", "webp", "avif"]);
59932
60240
  async function buildSite(input2) {
59933
60241
  const options2 = { ...DEFAULT_OPTIONS, ...input2.options || {} };
59934
60242
  const state = await createBuildState(input2, options2);
@@ -59976,7 +60284,9 @@ async function buildSite(input2) {
59976
60284
  await writeOutput(state.writer, state.summaries, "sitemap.xml", buildSitemapXml(state.previewData.site, state.emitted, state.generatedAt), "application/xml");
59977
60285
  await writeOutput(state.writer, state.summaries, "feed.xml", buildFeedXml(state.previewData.site, state.emitted, state.generatedAt), "application/rss+xml");
59978
60286
  }
59979
- await writeOutput(state.writer, state.summaries, "robots.txt", buildRobotsTxt(state.previewData.site), "text/plain");
60287
+ if (shouldGenerateRobotsTxt(options2)) {
60288
+ await writeOutput(state.writer, state.summaries, "robots.txt", buildRobotsTxt(state.previewData.site), "text/plain");
60289
+ }
59980
60290
  }
59981
60291
  return finalizeBuildResult(state.writer, state.summaries, options2);
59982
60292
  }
@@ -59989,7 +60299,7 @@ async function createBuildState(input2, options2) {
59989
60299
  const engine = new ZeroPressEngine();
59990
60300
  const assetProcessor = new AssetProcessor();
59991
60301
  const summaries = [];
59992
- const previewData = normalizePreviewData(input2.previewData);
60302
+ const previewData = normalizePreviewData(input2.previewData, options2);
59993
60303
  const renderData = createRenderData(previewData, themePackage.metadata);
59994
60304
  engine.initialize(themePackage);
59995
60305
  const assetOutputs = await buildAssetOutputs(themePackage.assets, assetProcessor, options2);
@@ -60012,6 +60322,7 @@ async function createBuildState(input2, options2) {
60012
60322
  assetMap,
60013
60323
  customCssHref: customCssAsset ? `/${customCssAsset.path}` : "",
60014
60324
  customHtml: previewData.custom_html,
60325
+ favicon: previewData.site.favicon,
60015
60326
  commentPolicyContent: buildCommentPolicyManifest(renderData.posts),
60016
60327
  options: options2,
60017
60328
  generatedAt: /* @__PURE__ */ new Date(),
@@ -60068,6 +60379,7 @@ async function renderRoute(state, templateName, route) {
60068
60379
  {
60069
60380
  menus: state.previewData.menus,
60070
60381
  widgets: state.widgets,
60382
+ collections: state.renderData.collections,
60071
60383
  taxonomies: state.renderData.taxonomies,
60072
60384
  ...route,
60073
60385
  route: routeContext,
@@ -60111,6 +60423,7 @@ async function renderFrontPage(state, route) {
60111
60423
  {
60112
60424
  menus: state.previewData.menus,
60113
60425
  widgets: state.widgets,
60426
+ collections: state.renderData.collections,
60114
60427
  taxonomies: state.renderData.taxonomies,
60115
60428
  page,
60116
60429
  route: routeContext,
@@ -60140,6 +60453,7 @@ async function renderFrontPage(state, route) {
60140
60453
  {
60141
60454
  menus: state.previewData.menus,
60142
60455
  widgets: state.widgets,
60456
+ collections: state.renderData.collections,
60143
60457
  taxonomies: state.renderData.taxonomies,
60144
60458
  ...route,
60145
60459
  route: routeContext,
@@ -60169,6 +60483,7 @@ async function renderPost(state, post) {
60169
60483
  {
60170
60484
  menus: state.previewData.menus,
60171
60485
  widgets: state.widgets,
60486
+ collections: state.renderData.collections,
60172
60487
  taxonomies: state.renderData.taxonomies,
60173
60488
  post,
60174
60489
  route: buildRouteContext("post", currentUrl),
@@ -60205,6 +60520,7 @@ async function renderPage(state, page) {
60205
60520
  {
60206
60521
  menus: state.previewData.menus,
60207
60522
  widgets: state.widgets,
60523
+ collections: state.renderData.collections,
60208
60524
  taxonomies: state.renderData.taxonomies,
60209
60525
  page,
60210
60526
  route: buildRouteContext("page", currentUrl),
@@ -60239,6 +60555,7 @@ async function maybeRenderNotFoundPage(state) {
60239
60555
  {
60240
60556
  menus: state.previewData.menus,
60241
60557
  widgets: state.widgets,
60558
+ collections: state.renderData.collections,
60242
60559
  taxonomies: state.renderData.taxonomies,
60243
60560
  route: buildRouteContext("not_found", "/404.html"),
60244
60561
  meta: buildPageMeta(state.previewData.site, {
@@ -60254,48 +60571,183 @@ async function maybeRenderNotFoundPage(state) {
60254
60571
  html = injectSiteCustomizations(html, state);
60255
60572
  await writeOutput(state.writer, state.summaries, "404.html", html, "text/html");
60256
60573
  }
60257
- function normalizePreviewData(previewData) {
60574
+ function normalizePreviewData(previewData, options2 = {}) {
60258
60575
  const normalizedSite = {
60259
60576
  ...previewData.site,
60260
60577
  mediaBaseUrl: normalizeOptionalString(previewData.site.mediaBaseUrl),
60578
+ mediaDeliveryMode: MEDIA_DELIVERY_MODES.has(previewData.site.mediaDeliveryMode) ? previewData.site.mediaDeliveryMode : "none",
60579
+ favicon: normalizeSiteFavicon(previewData.site.favicon || options2.favicon),
60261
60580
  postsPerPage: Number.isInteger(previewData.site.postsPerPage) && previewData.site.postsPerPage > 0 ? previewData.site.postsPerPage : DEFAULT_POSTS_PER_PAGE,
60262
60581
  dateFormat: normalizeNonEmptyString(previewData.site.dateFormat, DEFAULT_DATE_FORMAT),
60263
60582
  timeFormat: typeof previewData.site.timeFormat === "string" ? previewData.site.timeFormat : DEFAULT_TIME_FORMAT,
60264
60583
  timezone: normalizeNonEmptyString(previewData.site.timezone, DEFAULT_TIMEZONE),
60265
60584
  locale: normalizeLocale(previewData.site.locale || DEFAULT_LOCALE),
60266
60585
  disallowComments: previewData.site.disallowComments === true,
60586
+ indexing: previewData.site.indexing !== false,
60267
60587
  permalinks: normalizePermalinks(previewData.site.permalinks),
60268
60588
  front_page: normalizeFrontPage(previewData.site.front_page),
60269
60589
  post_index: normalizePostIndex(previewData.site.post_index),
60270
60590
  footer: normalizeSiteFooter(previewData.site.footer)
60271
60591
  };
60592
+ const media = normalizeContentMedia(previewData.content.media, normalizedSite);
60593
+ const mediaRegistry = buildMediaRegistry(media);
60272
60594
  return {
60273
60595
  ...previewData,
60274
60596
  site: normalizedSite,
60597
+ menus: normalizeRecordMap(previewData.menus),
60598
+ collections: normalizeCollections(previewData.collections),
60275
60599
  widgets: normalizeWidgetAreas(previewData.widgets, normalizedSite.mediaBaseUrl),
60276
60600
  custom_css: normalizeCustomCss(previewData.custom_css),
60277
60601
  custom_html: normalizeCustomHtml(previewData.custom_html),
60278
60602
  content: {
60279
60603
  ...previewData.content,
60280
- authors: previewData.content.authors.map((author) => ({
60281
- ...author,
60282
- avatar: normalizeMediaField(author.avatar, normalizedSite.mediaBaseUrl)
60283
- })),
60284
- posts: previewData.content.posts.map((post) => ({
60285
- ...post,
60286
- published_at_iso: normalizeIsoTimestamp(post.published_at_iso),
60287
- updated_at_iso: normalizeIsoTimestamp(post.updated_at_iso),
60288
- featured_image: normalizeMediaField(post.featured_image, normalizedSite.mediaBaseUrl)
60289
- })).sort((left, right) => toDate(right.published_at_iso).getTime() - toDate(left.published_at_iso).getTime()),
60290
- pages: previewData.content.pages.map((page) => ({
60291
- ...page,
60292
- featured_image: normalizeMediaField(page.featured_image, normalizedSite.mediaBaseUrl)
60293
- })),
60604
+ authors: previewData.content.authors.map((author) => {
60605
+ const avatar = normalizeMediaField(author.avatar, normalizedSite.mediaBaseUrl);
60606
+ const avatarMedia = deriveManagedMedia(avatar, mediaRegistry, normalizedSite);
60607
+ return {
60608
+ ...author,
60609
+ avatar,
60610
+ ...avatarMedia ? { avatar_media: avatarMedia } : {}
60611
+ };
60612
+ }),
60613
+ posts: previewData.content.posts.map((post) => {
60614
+ const featuredImage = normalizeMediaField(post.featured_image, normalizedSite.mediaBaseUrl);
60615
+ const featuredMedia = deriveManagedMedia(featuredImage, mediaRegistry, normalizedSite);
60616
+ return {
60617
+ ...post,
60618
+ published_at_iso: normalizeIsoTimestamp(post.published_at_iso),
60619
+ updated_at_iso: normalizeIsoTimestamp(post.updated_at_iso),
60620
+ featured_image: featuredImage,
60621
+ ...featuredMedia ? { featured_media: featuredMedia } : {}
60622
+ };
60623
+ }).sort((left, right) => toDate(right.published_at_iso).getTime() - toDate(left.published_at_iso).getTime()),
60624
+ pages: previewData.content.pages.map((page) => {
60625
+ const featuredImage = normalizeMediaField(page.featured_image, normalizedSite.mediaBaseUrl);
60626
+ const featuredMedia = deriveManagedMedia(featuredImage, mediaRegistry, normalizedSite);
60627
+ return {
60628
+ ...page,
60629
+ featured_image: featuredImage,
60630
+ ...featuredMedia ? { featured_media: featuredMedia } : {}
60631
+ };
60632
+ }),
60294
60633
  categories: [...previewData.content.categories],
60295
- tags: [...previewData.content.tags]
60634
+ tags: [...previewData.content.tags],
60635
+ media
60636
+ }
60637
+ };
60638
+ }
60639
+ function normalizeRecordMap(value) {
60640
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
60641
+ return {};
60642
+ }
60643
+ return { ...value };
60644
+ }
60645
+ function normalizeContentMedia(mediaItems, site) {
60646
+ if (!Array.isArray(mediaItems)) {
60647
+ return [];
60648
+ }
60649
+ return mediaItems.map((item) => {
60650
+ if (!item || typeof item !== "object") {
60651
+ return null;
60296
60652
  }
60653
+ const src = normalizeMediaField(item.src, site.mediaBaseUrl);
60654
+ const width = Number.isInteger(item.width) && item.width > 0 ? item.width : 0;
60655
+ const height = Number.isInteger(item.height) && item.height > 0 ? item.height : 0;
60656
+ if (!src || !width || !height) {
60657
+ return null;
60658
+ }
60659
+ return {
60660
+ src,
60661
+ width,
60662
+ height,
60663
+ alt: typeof item.alt === "string" ? item.alt : ""
60664
+ };
60665
+ }).filter(Boolean);
60666
+ }
60667
+ function buildMediaRegistry(mediaItems) {
60668
+ const registry = /* @__PURE__ */ new Map();
60669
+ for (const item of mediaItems) {
60670
+ if (!registry.has(item.src)) {
60671
+ registry.set(item.src, item);
60672
+ }
60673
+ }
60674
+ return registry;
60675
+ }
60676
+ function deriveManagedMedia(src, mediaRegistry, site) {
60677
+ const normalizedSrc = normalizeOptionalString(src);
60678
+ if (!normalizedSrc) {
60679
+ return null;
60680
+ }
60681
+ const media = mediaRegistry.get(normalizedSrc);
60682
+ if (!media) {
60683
+ return null;
60684
+ }
60685
+ return {
60686
+ ...media,
60687
+ srcset: buildResponsiveImageSrcset(media, site)
60297
60688
  };
60298
60689
  }
60690
+ function buildResponsiveImageSrcset(media, site) {
60691
+ if (site.mediaDeliveryMode !== "media_domain") {
60692
+ return "";
60693
+ }
60694
+ const mediaBaseUrl = normalizeOptionalString(site.mediaBaseUrl);
60695
+ if (!mediaBaseUrl || !isUrlUnderMediaBase(media.src, mediaBaseUrl) || !isResponsiveRasterImage(media.src)) {
60696
+ return "";
60697
+ }
60698
+ const widths = RESPONSIVE_IMAGE_WIDTHS.filter((width) => width <= media.width);
60699
+ if (!widths.includes(media.width)) {
60700
+ widths.push(media.width);
60701
+ }
60702
+ return widths.filter((width, index, values) => width > 0 && values.indexOf(width) === index).map((width) => `${buildResponsiveImageVariantUrl(media.src, width)} ${width}w`).join(", ");
60703
+ }
60704
+ function buildResponsiveImageVariantUrl(src, width) {
60705
+ try {
60706
+ const url = new URL(src);
60707
+ url.searchParams.set("w", String(width));
60708
+ url.searchParams.set("fit", "scale-down");
60709
+ url.searchParams.set("format", "auto");
60710
+ return decodeURI(url.toString());
60711
+ } catch {
60712
+ return src;
60713
+ }
60714
+ }
60715
+ function isUrlUnderMediaBase(src, mediaBaseUrl) {
60716
+ try {
60717
+ const sourceUrl = new URL(src);
60718
+ const baseUrl = new URL(mediaBaseUrl);
60719
+ const basePath = baseUrl.pathname.endsWith("/") ? baseUrl.pathname : `${baseUrl.pathname}/`;
60720
+ return sourceUrl.origin === baseUrl.origin && sourceUrl.pathname.startsWith(basePath);
60721
+ } catch {
60722
+ return false;
60723
+ }
60724
+ }
60725
+ function isResponsiveRasterImage(src) {
60726
+ try {
60727
+ const url = new URL(src);
60728
+ const lastSegment = url.pathname.split("/").pop() || "";
60729
+ const extension = lastSegment.includes(".") ? lastSegment.split(".").pop().toLowerCase() : "";
60730
+ return RESPONSIVE_IMAGE_EXTENSIONS.has(extension);
60731
+ } catch {
60732
+ return false;
60733
+ }
60734
+ }
60735
+ function normalizeCollections(collections) {
60736
+ if (!collections || typeof collections !== "object" || Array.isArray(collections)) {
60737
+ return {};
60738
+ }
60739
+ return Object.fromEntries(
60740
+ Object.entries(collections).map(([collectionId, collection]) => [
60741
+ collectionId,
60742
+ {
60743
+ ...collection,
60744
+ title: normalizeOptionalString(collection?.title),
60745
+ description: normalizeOptionalString(collection?.description),
60746
+ items: Array.isArray(collection?.items) ? collection.items.map((item) => ({ ...item })) : []
60747
+ }
60748
+ ])
60749
+ );
60750
+ }
60299
60751
  function normalizeWidgetAreas(widgetAreas, mediaBaseUrl) {
60300
60752
  if (!widgetAreas || typeof widgetAreas !== "object") {
60301
60753
  return {};
@@ -60357,6 +60809,19 @@ function normalizeCustomHtml(customHtml) {
60357
60809
  ...bodyEnd ? { body_end: { content: bodyEnd } } : {}
60358
60810
  };
60359
60811
  }
60812
+ function normalizeSiteFavicon(favicon) {
60813
+ if (!favicon || typeof favicon !== "object") {
60814
+ return void 0;
60815
+ }
60816
+ const normalized = {};
60817
+ for (const key of ["icon", "svg", "png", "apple_touch_icon"]) {
60818
+ const value = normalizeOptionalString(favicon[key]);
60819
+ if (value) {
60820
+ normalized[key] = value;
60821
+ }
60822
+ }
60823
+ return Object.keys(normalized).length ? normalized : void 0;
60824
+ }
60360
60825
  function normalizePermalinks(permalinks) {
60361
60826
  const source = permalinks && typeof permalinks === "object" ? permalinks : {};
60362
60827
  const outputStyle = typeof source.output_style === "string" && PERMALINK_OUTPUT_STYLES.has(source.output_style) ? source.output_style : DEFAULT_PERMALINKS.output_style;
@@ -60417,6 +60882,7 @@ function createRenderData(previewData, themeMetadata = {}) {
60417
60882
  }));
60418
60883
  const pages = previewData.content.pages.map((page) => preparePage(page, previewData.site));
60419
60884
  const postBySlug = new Map(posts.map((post) => [post.slug, post]));
60885
+ const pageBySlug = new Map(pages.map((page) => [page.slug, page]));
60420
60886
  const frontPage = previewData.site.front_page;
60421
60887
  const postIndex = previewData.site.post_index;
60422
60888
  const effectivePostIndexEnabled = postIndex.enabled !== false && themeSupportsPostIndex;
@@ -60432,6 +60898,7 @@ function createRenderData(previewData, themeMetadata = {}) {
60432
60898
  posts,
60433
60899
  pages: preparedPages,
60434
60900
  postBySlug,
60901
+ collections: resolveCollections(previewData.collections, postBySlug, pageBySlug, frontPage),
60435
60902
  taxonomies: buildGlobalTaxonomies(previewData, categoryCountBySlug, tagCountBySlug),
60436
60903
  frontPageRoute,
60437
60904
  indexRoutes: buildPostIndexRoutes({
@@ -60488,6 +60955,61 @@ function buildGlobalTaxonomies(previewData, categoryCountBySlug, tagCountBySlug)
60488
60955
  tags: buildGlobalTaxonomyItems(previewData.site, "tags", previewData.content.tags, tagCountBySlug)
60489
60956
  };
60490
60957
  }
60958
+ function resolveCollections(collections, postBySlug, pageBySlug, frontPage) {
60959
+ if (!collections || typeof collections !== "object") {
60960
+ return {};
60961
+ }
60962
+ return Object.fromEntries(
60963
+ Object.entries(collections).map(([collectionId, collection]) => [
60964
+ collectionId,
60965
+ {
60966
+ id: collectionId,
60967
+ title: normalizeOptionalString(collection?.title),
60968
+ description: normalizeOptionalString(collection?.description),
60969
+ items: resolveCollectionItems(collectionId, collection?.items, postBySlug, pageBySlug, frontPage)
60970
+ }
60971
+ ])
60972
+ );
60973
+ }
60974
+ function resolveCollectionItems(collectionId, items, postBySlug, pageBySlug, frontPage) {
60975
+ if (!Array.isArray(items)) {
60976
+ return [];
60977
+ }
60978
+ return items.map((item, index) => resolveCollectionItem(collectionId, item, index, postBySlug, pageBySlug, frontPage));
60979
+ }
60980
+ function resolveCollectionItem(collectionId, item, index, postBySlug, pageBySlug, frontPage) {
60981
+ if (item?.type === "post") {
60982
+ const post = postBySlug.get(item.slug);
60983
+ if (!post) {
60984
+ throw new Error(`Invalid collection "${collectionId}": item ${index + 1} references missing post slug "${item.slug}".`);
60985
+ }
60986
+ return {
60987
+ type: "post",
60988
+ meta: post.meta,
60989
+ ...buildStructuredPostSummary(post)
60990
+ };
60991
+ }
60992
+ if (item?.type === "page") {
60993
+ const page = pageBySlug.get(item.slug);
60994
+ if (!page) {
60995
+ throw new Error(`Invalid collection "${collectionId}": item ${index + 1} references missing page slug "${item.slug}".`);
60996
+ }
60997
+ return buildCollectionPageSummary(page, frontPage);
60998
+ }
60999
+ throw new Error(`Invalid collection "${collectionId}": item ${index + 1} has unsupported type "${item?.type}".`);
61000
+ }
61001
+ function buildCollectionPageSummary(page, frontPage) {
61002
+ return {
61003
+ type: "page",
61004
+ title: page.title,
61005
+ slug: page.slug,
61006
+ url: frontPage?.type === "page" && frontPage.page_slug === page.slug ? "/" : page.url,
61007
+ excerpt: page.excerpt || "",
61008
+ featured_image: page.featured_image || "",
61009
+ ...page.featured_media ? { featured_media: { ...page.featured_media } } : {},
61010
+ meta: page.meta
61011
+ };
61012
+ }
60491
61013
  function buildFrontPageRoute(frontPage, pages, effectivePostIndexEnabled, postIndexBasePath) {
60492
61014
  if (frontPage.type === "theme_index") {
60493
61015
  if (effectivePostIndexEnabled && postIndexBasePath === "/") {
@@ -60788,6 +61310,7 @@ function preparePost(post, site, authorsById, categoriesBySlug, tagsBySlug, them
60788
61310
  updated_at_iso: post.updated_at_iso,
60789
61311
  author_id: post.author_id,
60790
61312
  featured_image: post.featured_image,
61313
+ ...post.featured_media ? { featured_media: { ...post.featured_media } } : {},
60791
61314
  meta: post.meta,
60792
61315
  status: post.status,
60793
61316
  allow_comments: post.allow_comments,
@@ -60796,7 +61319,8 @@ function preparePost(post, site, authorsById, categoriesBySlug, tagsBySlug, them
60796
61319
  author: {
60797
61320
  id: post.author_id,
60798
61321
  display_name: normalizeNonEmptyString(author?.display_name, post.author_id),
60799
- avatar: author?.avatar || ""
61322
+ avatar: author?.avatar || "",
61323
+ ...author?.avatar_media ? { avatar_media: { ...author.avatar_media } } : {}
60800
61324
  },
60801
61325
  categories,
60802
61326
  tags,
@@ -60998,9 +61522,11 @@ function buildStructuredPostSummary(post) {
60998
61522
  published_at_iso: post.published_at_iso,
60999
61523
  reading_time: post.reading_time,
61000
61524
  featured_image: post.featured_image,
61525
+ ...post.featured_media ? { featured_media: { ...post.featured_media } } : {},
61001
61526
  author: {
61002
61527
  display_name: post.author?.display_name || "",
61003
- avatar: post.author?.avatar || ""
61528
+ avatar: post.author?.avatar || "",
61529
+ ...post.author?.avatar_media ? { avatar_media: { ...post.author.avatar_media } } : {}
61004
61530
  },
61005
61531
  categories: Array.isArray(post.categories) ? post.categories.map((category) => ({ ...category })) : [],
61006
61532
  tags: Array.isArray(post.tags) ? post.tags.map((tag) => ({ ...tag })) : []
@@ -61206,7 +61732,8 @@ async function normalizeAndValidateThemePackage(themePackage) {
61206
61732
  ...themePackage.metadata.features ? { features: themePackage.metadata.features } : {},
61207
61733
  ...themePackage.metadata.menuSlots ? { menuSlots: themePackage.metadata.menuSlots } : {},
61208
61734
  ...themePackage.metadata.widgetAreas ? { widgetAreas: themePackage.metadata.widgetAreas } : {},
61209
- settings: themePackage.metadata.settings || {}
61735
+ ...themePackage.metadata.siteMeta ? { siteMeta: themePackage.metadata.siteMeta } : {},
61736
+ ...themePackage.metadata.collectionSlots ? { collectionSlots: themePackage.metadata.collectionSlots } : {}
61210
61737
  }));
61211
61738
  for (const [templateName, templateContent] of themePackage.templates.entries()) {
61212
61739
  fileMap.set(`${templateName}.html`, templateContent);
@@ -61234,8 +61761,7 @@ async function normalizeAndValidateThemePackage(themePackage) {
61234
61761
  function normalizeThemePackageMetadata(sourceMetadata, manifest) {
61235
61762
  return {
61236
61763
  ...manifest,
61237
- ...sourceMetadata?.thumbnail ? { thumbnail: sourceMetadata.thumbnail } : {},
61238
- settings: sourceMetadata?.settings || {}
61764
+ ...sourceMetadata?.thumbnail ? { thumbnail: sourceMetadata.thumbnail } : {}
61239
61765
  };
61240
61766
  }
61241
61767
  async function buildAssetOutputs(assets, assetProcessor, options2) {
@@ -61382,7 +61908,7 @@ function normalizeMediaField(value, mediaBaseUrl) {
61382
61908
  }
61383
61909
  const normalizedBaseUrl = normalizeOptionalString(mediaBaseUrl);
61384
61910
  if (!normalizedBaseUrl) {
61385
- return "";
61911
+ return normalizedValue;
61386
61912
  }
61387
61913
  try {
61388
61914
  return decodeURI(new URL(normalizedValue, normalizedBaseUrl).toString());
@@ -61586,7 +62112,10 @@ function assertPlannedOutputPathsSafe(state) {
61586
62112
  COMMENT_POLICY_OUTPUT_PATH
61587
62113
  ];
61588
62114
  if (state.options.generateSpecialFiles) {
61589
- plannedPaths.push("404.html", "robots.txt");
62115
+ plannedPaths.push("404.html");
62116
+ if (shouldGenerateRobotsTxt(state.options)) {
62117
+ plannedPaths.push("robots.txt");
62118
+ }
61590
62119
  if (hasCanonicalSiteUrl(state.previewData.site.url)) {
61591
62120
  plannedPaths.push("sitemap.xml", "feed.xml");
61592
62121
  }
@@ -61661,10 +62190,38 @@ function sha256(content) {
61661
62190
  return hash.digest("hex");
61662
62191
  }
61663
62192
  function injectSiteCustomizations(html, state) {
61664
- let next = injectCustomCssAssetLink(html, state.customCssHref);
62193
+ let next = injectFaviconLinks(html, state.favicon);
62194
+ next = injectCustomCssAssetLink(next, state.customCssHref);
61665
62195
  next = injectCustomHtml(next, state.customHtml);
61666
62196
  return next;
61667
62197
  }
62198
+ function injectFaviconLinks(html, favicon) {
62199
+ const links = buildFaviconLinks(favicon);
62200
+ if (!links) {
62201
+ return html;
62202
+ }
62203
+ return html.replace("</head>", `${links}
62204
+ </head>`);
62205
+ }
62206
+ function buildFaviconLinks(favicon) {
62207
+ if (!favicon || typeof favicon !== "object") {
62208
+ return "";
62209
+ }
62210
+ const lines = [];
62211
+ if (normalizeOptionalString(favicon.icon)) {
62212
+ lines.push(` <link rel="icon" href="${escapeHtml2(favicon.icon)}" sizes="any">`);
62213
+ }
62214
+ if (normalizeOptionalString(favicon.svg)) {
62215
+ lines.push(` <link rel="icon" href="${escapeHtml2(favicon.svg)}" type="image/svg+xml">`);
62216
+ }
62217
+ if (normalizeOptionalString(favicon.png)) {
62218
+ lines.push(` <link rel="icon" href="${escapeHtml2(favicon.png)}" type="image/png">`);
62219
+ }
62220
+ if (normalizeOptionalString(favicon.apple_touch_icon)) {
62221
+ lines.push(` <link rel="apple-touch-icon" href="${escapeHtml2(favicon.apple_touch_icon)}">`);
62222
+ }
62223
+ return lines.join("\n");
62224
+ }
61668
62225
  function injectCustomCssAssetLink(html, href) {
61669
62226
  if (!normalizeOptionalString(href)) {
61670
62227
  return html;
@@ -61752,13 +62309,22 @@ ${items}
61752
62309
  </rss>`;
61753
62310
  }
61754
62311
  function buildRobotsTxt(site) {
61755
- const lines = ["User-agent: *", "Allow: /"];
62312
+ const lines = ["User-agent: *"];
62313
+ if (site.indexing === false) {
62314
+ lines.push("Disallow: /");
62315
+ return `${lines.join("\n")}
62316
+ `;
62317
+ }
62318
+ lines.push("Allow: /");
61756
62319
  if (site.url) {
61757
62320
  lines.push("", `Sitemap: ${resolveSiteUrl(site.url, "/sitemap.xml")}`);
61758
62321
  }
61759
62322
  return `${lines.join("\n")}
61760
62323
  `;
61761
62324
  }
62325
+ function shouldGenerateRobotsTxt(options2) {
62326
+ return options2.generateSpecialFiles && options2.generateRobotsTxt !== false;
62327
+ }
61762
62328
  function getContentType(assetPath) {
61763
62329
  const ext = assetPath.split(".").pop()?.toLowerCase();
61764
62330
  const contentTypes = {
@@ -61867,8 +62433,7 @@ async function loadThemePackageFromDir(themeDir) {
61867
62433
  return {
61868
62434
  metadata: {
61869
62435
  ...manifest,
61870
- thumbnail: themeJson.thumbnail,
61871
- settings: themeJson.settings || {}
62436
+ thumbnail: themeJson.thumbnail
61872
62437
  },
61873
62438
  templates,
61874
62439
  partials,
@@ -61915,6 +62480,12 @@ var require2 = createRequire(import.meta.url);
61915
62480
  var { version: PACKAGE_VERSION } = require2("../package.json");
61916
62481
  var DEFAULT_PUBLIC_DIR_NAME = "public";
61917
62482
  var PUBLIC_DIR_ENV_NAME = "ZEROPRESS_PUBLIC_DIR";
62483
+ var PUBLIC_FAVICON_FILES = Object.freeze({
62484
+ icon: "favicon.ico",
62485
+ svg: "favicon.svg",
62486
+ png: "favicon.png",
62487
+ apple_touch_icon: "apple-touch-icon.png"
62488
+ });
61918
62489
  async function assertThemeDirectory(themeDir) {
61919
62490
  let stat;
61920
62491
  try {
@@ -61934,12 +62505,19 @@ async function runBuild(themeDir, previewData, outDir) {
61934
62505
  assertPublicPathDoesNotOverlap("Output directory", outDir);
61935
62506
  await assertThemeDirectory(themeDir);
61936
62507
  await assertEmptyOutputDirectory(outDir);
61937
- await copyPublicDirectory(resolvePublicDir(), outDir);
62508
+ const publicDir = resolvePublicDir();
62509
+ const hasPublicRobotsTxt = await publicRobotsTxtExists(publicDir);
62510
+ const publicFavicon = await discoverPublicFavicon(publicDir);
62511
+ await copyPublicDirectory(publicDir, outDir);
61938
62512
  const writer = new GeneratedOutputWriter({ outDir });
61939
62513
  return buildSiteFromThemeDir({
61940
62514
  previewData,
61941
62515
  themeDir,
61942
- writer
62516
+ writer,
62517
+ options: {
62518
+ favicon: publicFavicon,
62519
+ generateRobotsTxt: !hasPublicRobotsTxt
62520
+ }
61943
62521
  });
61944
62522
  }
61945
62523
  var GeneratedOutputWriter = class {
@@ -61996,6 +62574,36 @@ async function copyPublicDirectory(publicDir, outDir) {
61996
62574
  }
61997
62575
  await copyPublicEntries(publicDir, outDir);
61998
62576
  }
62577
+ async function publicRobotsTxtExists(publicDir) {
62578
+ let stat;
62579
+ try {
62580
+ stat = await fs.lstat(path.join(publicDir, "robots.txt"));
62581
+ } catch (error) {
62582
+ if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
62583
+ return false;
62584
+ }
62585
+ throw error;
62586
+ }
62587
+ return stat.isFile();
62588
+ }
62589
+ async function discoverPublicFavicon(publicDir) {
62590
+ const favicon = {};
62591
+ for (const [key, filename] of Object.entries(PUBLIC_FAVICON_FILES)) {
62592
+ let stat;
62593
+ try {
62594
+ stat = await fs.lstat(path.join(publicDir, filename));
62595
+ } catch (error) {
62596
+ if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
62597
+ continue;
62598
+ }
62599
+ throw error;
62600
+ }
62601
+ if (stat.isFile()) {
62602
+ favicon[key] = `/${filename}`;
62603
+ }
62604
+ }
62605
+ return Object.keys(favicon).length ? favicon : void 0;
62606
+ }
61999
62607
  async function copyPublicEntries(sourceDir, targetDir) {
62000
62608
  const entries = await fs.readdir(sourceDir, { withFileTypes: true });
62001
62609
  for (const entry of entries) {
@@ -62145,6 +62753,7 @@ var STAGING_DIR = ".zeropress/public-assets";
62145
62753
  var DEFAULT_THEME = "docs";
62146
62754
  async function runBuildPages(options2) {
62147
62755
  const cwd = path3.resolve(options2.cwd || process.cwd());
62756
+ const copyMarkdownSource = options2.copyMarkdownSource !== false;
62148
62757
  const sourceDir = path3.resolve(cwd, options2.source);
62149
62758
  const destinationDir = path3.resolve(cwd, options2.destination);
62150
62759
  const generatedDir = path3.join(cwd, ".zeropress");
@@ -62165,7 +62774,8 @@ async function runBuildPages(options2) {
62165
62774
  ...process.env,
62166
62775
  ZEROPRESS_BUILD_PAGES_SOURCE: sourceDir,
62167
62776
  ZEROPRESS_PUBLIC_DIR: sourceDir,
62168
- ZEROPRESS_SKIP_UNTITLED_MARKDOWN: String(Boolean(options2.skipUntitledMarkdown))
62777
+ ZEROPRESS_SKIP_UNTITLED_MARKDOWN: String(Boolean(options2.skipUntitledMarkdown)),
62778
+ ZEROPRESS_COPY_MARKDOWN_SOURCE: String(copyMarkdownSource)
62169
62779
  };
62170
62780
  if (options2.config) {
62171
62781
  env.ZEROPRESS_BUILD_PAGES_CONFIG = path3.resolve(cwd, options2.config);
@@ -62188,7 +62798,8 @@ async function runBuildPages(options2) {
62188
62798
  await fs3.rm(stagingDir, { recursive: true, force: true });
62189
62799
  await fs3.mkdir(stagingDir, { recursive: true });
62190
62800
  await copyPublicStaging(sourceDir, stagingDir, {
62191
- excludePaths: [destinationDir, themeDir, generatedDir]
62801
+ excludePaths: [destinationDir, themeDir, generatedDir],
62802
+ copyMarkdownSource
62192
62803
  });
62193
62804
  const previousPublicDir = process.env.ZEROPRESS_PUBLIC_DIR;
62194
62805
  process.env.ZEROPRESS_PUBLIC_DIR = stagingDir;
@@ -62277,6 +62888,9 @@ async function copyPublicStaging(sourceDir, targetDir, options2) {
62277
62888
  if (!entry.isFile()) {
62278
62889
  continue;
62279
62890
  }
62891
+ if (options2.copyMarkdownSource === false && entry.name.toLowerCase().endsWith(".md")) {
62892
+ continue;
62893
+ }
62280
62894
  await fs3.mkdir(path3.dirname(targetPath), { recursive: true });
62281
62895
  await fs3.copyFile(sourcePath, targetPath);
62282
62896
  }
@@ -62315,7 +62929,8 @@ var options = {
62315
62929
  config: input("config"),
62316
62930
  siteUrl: input("site-url"),
62317
62931
  skipUntitledMarkdown: booleanInput("skip-untitled-markdown", false),
62318
- skipLinkCheck: booleanInput("skip-link-check", false)
62932
+ skipLinkCheck: booleanInput("skip-link-check", false),
62933
+ copyMarkdownSource: falseOnlyInput("copy-markdown-source")
62319
62934
  };
62320
62935
  try {
62321
62936
  await runBuildPages(options);
@@ -62334,3 +62949,6 @@ function booleanInput(name, fallback) {
62334
62949
  }
62335
62950
  return value.toLowerCase() === "true";
62336
62951
  }
62952
+ function falseOnlyInput(name) {
62953
+ return input(name).toLowerCase() !== "false";
62954
+ }