@nuxtjs/sitemap 7.2.9 → 7.3.0

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.
Files changed (52) hide show
  1. package/dist/client/200.html +9 -9
  2. package/dist/client/404.html +9 -9
  3. package/dist/client/_nuxt/Cp-IABpG.js +1 -0
  4. package/dist/client/_nuxt/DJVkgDQ2.js +1 -0
  5. package/dist/client/_nuxt/SmY-NWqO.js +172 -0
  6. package/dist/client/_nuxt/builds/latest.json +1 -1
  7. package/dist/client/_nuxt/builds/meta/e48bfd5b-6605-4bbc-a466-32a664787616.json +1 -0
  8. package/dist/client/_nuxt/{entry.CJ2Eg9q-.css → entry.CgW0_noo.css} +1 -1
  9. package/dist/client/_nuxt/error-404.CtcyoHAN.css +1 -0
  10. package/dist/client/_nuxt/error-500.BIlfyoPk.css +1 -0
  11. package/dist/client/_nuxt/lL_X76lO.js +1 -0
  12. package/dist/client/index.html +9 -9
  13. package/dist/content.d.cts +2 -1
  14. package/dist/content.d.mts +2 -1
  15. package/dist/content.d.ts +2 -1
  16. package/dist/module.cjs +83 -42
  17. package/dist/module.d.cts +3 -1
  18. package/dist/module.d.mts +3 -1
  19. package/dist/module.d.ts +3 -1
  20. package/dist/module.json +3 -3
  21. package/dist/module.mjs +84 -43
  22. package/dist/runtime/server/plugins/warm-up.js +20 -4
  23. package/dist/runtime/server/routes/__sitemap__/debug.d.ts +4 -4
  24. package/dist/runtime/server/routes/__sitemap__/debug.js +2 -2
  25. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.js +7 -2
  26. package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +37 -7
  27. package/dist/runtime/server/routes/sitemap_index.xml.js +11 -3
  28. package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +1 -1
  29. package/dist/runtime/server/sitemap/builder/sitemap-index.js +110 -16
  30. package/dist/runtime/server/sitemap/builder/sitemap.d.ts +1 -1
  31. package/dist/runtime/server/sitemap/builder/sitemap.js +62 -36
  32. package/dist/runtime/server/sitemap/builder/xml.d.ts +2 -3
  33. package/dist/runtime/server/sitemap/builder/xml.js +182 -80
  34. package/dist/runtime/server/sitemap/nitro.js +68 -20
  35. package/dist/runtime/server/sitemap/urlset/normalise.js +21 -19
  36. package/dist/runtime/server/sitemap/urlset/sort.d.ts +1 -1
  37. package/dist/runtime/server/sitemap/urlset/sort.js +9 -15
  38. package/dist/runtime/server/sitemap/utils/chunk.d.ts +10 -0
  39. package/dist/runtime/server/sitemap/utils/chunk.js +66 -0
  40. package/dist/runtime/types.d.ts +44 -0
  41. package/dist/runtime/utils-pure.js +13 -5
  42. package/dist/types.d.mts +5 -1
  43. package/package.json +32 -38
  44. package/content.d.ts +0 -1
  45. package/dist/client/_nuxt/BQoSv7ci.js +0 -1
  46. package/dist/client/_nuxt/BuImKM08.js +0 -1
  47. package/dist/client/_nuxt/DGiw9jHL.js +0 -172
  48. package/dist/client/_nuxt/LwtYuSjN.js +0 -1
  49. package/dist/client/_nuxt/builds/meta/491f5d1a-004d-407a-b464-c3363f77bc23.json +0 -1
  50. package/dist/client/_nuxt/error-404.CmL6pbzl.css +0 -1
  51. package/dist/client/_nuxt/error-500.CnmYQu0q.css +0 -1
  52. package/dist/types.d.ts +0 -1
package/dist/module.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useNuxt, loadNuxtModuleInstance, createResolver, addTemplate, extendPages, tryUseNuxt, defineNuxtModule, useLogger, hasNuxtModule, getNuxtModuleVersion, hasNuxtModuleCompatibility, addServerImports, addServerPlugin, resolveModule, addServerHandler, addPrerenderRoutes } from '@nuxt/kit';
2
- import { withHttps, withBase, parseURL, joinURL, withoutLeadingSlash, withLeadingSlash, withoutTrailingSlash } from 'ufo';
2
+ import { withHttps, withBase, parseURL, joinURL, withTrailingSlash, withoutLeadingSlash, withLeadingSlash, withoutTrailingSlash } from 'ufo';
3
3
  import { withSiteUrl, installNuxtSiteConfig } from 'nuxt-site-config/kit';
4
4
  import { defu } from 'defu';
5
5
  import { readPackageJSON } from 'pkg-types';
@@ -321,35 +321,25 @@ function extractSitemapMetaFromHtml(html, options) {
321
321
  description
322
322
  };
323
323
  const player_loc = (videoPlayerLocRegex.exec(videoTag) || [])[1];
324
- if (player_loc)
325
- videoObj.player_loc = player_loc;
324
+ if (player_loc) videoObj.player_loc = player_loc;
326
325
  const duration = (videoDurationRegex.exec(videoTag) || [])[1];
327
- if (duration)
328
- videoObj.duration = Number.parseInt(duration, 10);
326
+ if (duration) videoObj.duration = Number.parseInt(duration, 10);
329
327
  const expiration_date = (videoExpirationDateRegex.exec(videoTag) || [])[1];
330
- if (expiration_date)
331
- videoObj.expiration_date = expiration_date;
328
+ if (expiration_date) videoObj.expiration_date = expiration_date;
332
329
  const rating = (videoRatingRegex.exec(videoTag) || [])[1];
333
- if (rating)
334
- videoObj.rating = Number.parseFloat(rating);
330
+ if (rating) videoObj.rating = Number.parseFloat(rating);
335
331
  const view_count = (videoViewCountRegex.exec(videoTag) || [])[1];
336
- if (view_count)
337
- videoObj.view_count = Number.parseInt(view_count, 10);
332
+ if (view_count) videoObj.view_count = Number.parseInt(view_count, 10);
338
333
  const publication_date = (videoPublicationDateRegex.exec(videoTag) || [])[1];
339
- if (publication_date)
340
- videoObj.publication_date = publication_date;
334
+ if (publication_date) videoObj.publication_date = publication_date;
341
335
  const family_friendly = (videoFamilyFriendlyRegex.exec(videoTag) || [])[1];
342
- if (family_friendly)
343
- videoObj.family_friendly = family_friendly;
336
+ if (family_friendly) videoObj.family_friendly = family_friendly;
344
337
  const requires_subscription = (videoRequiresSubscriptionRegex.exec(videoTag) || [])[1];
345
- if (requires_subscription)
346
- videoObj.requires_subscription = requires_subscription;
338
+ if (requires_subscription) videoObj.requires_subscription = requires_subscription;
347
339
  const live = (videoLiveRegex.exec(videoTag) || [])[1];
348
- if (live)
349
- videoObj.live = live;
340
+ if (live) videoObj.live = live;
350
341
  const tag = (videoTagRegex.exec(videoTag) || [])[1];
351
- if (tag)
352
- videoObj.tag = tag;
342
+ if (tag) videoObj.tag = tag;
353
343
  const sources = [];
354
344
  let sourceMatch;
355
345
  while ((sourceMatch = sourceRegex.exec(videoContent)) !== null) {
@@ -653,7 +643,7 @@ const module = defineNuxtModule({
653
643
  defaults: {},
654
644
  // index sitemap options filtering
655
645
  include: [],
656
- exclude: ["/_nuxt/**", "/_**"],
646
+ exclude: ["/_**"],
657
647
  // sources
658
648
  sources: [],
659
649
  excludeAppSources: []
@@ -667,6 +657,7 @@ const module = defineNuxtModule({
667
657
  logger.debug("The module is disabled, skipping setup.");
668
658
  return;
669
659
  }
660
+ config.exclude.push(`${withTrailingSlash(nuxt.options.app.buildAssetsDir)}**`);
670
661
  nuxt.options.alias["#sitemap"] = resolve("./runtime");
671
662
  nuxt.options.nitro.alias = nuxt.options.nitro.alias || {};
672
663
  nuxt.options.nitro.alias["#sitemap"] = resolve("./runtime");
@@ -749,11 +740,11 @@ const module = defineNuxtModule({
749
740
  const locale = normalisedLocales.find((l) => l.code === localeCode);
750
741
  if (!locale || !pageLocales[localeCode] || pageLocales[localeCode].includes("["))
751
742
  continue;
752
- const alternatives = Object.keys(pageLocales).map((l) => ({
743
+ const alternatives = Object.keys(pageLocales).filter((l) => pageLocales[l] !== false).map((l) => ({
753
744
  hreflang: normalisedLocales.find((nl) => nl.code === l)?._hreflang || l,
754
745
  href: generatePathForI18nPages({ localeCode: l, pageLocales: pageLocales[l], nuxtI18nConfig, normalisedLocales })
755
746
  }));
756
- if (alternatives.length && nuxtI18nConfig.defaultLocale && pageLocales[nuxtI18nConfig.defaultLocale])
747
+ if (alternatives.length && nuxtI18nConfig.defaultLocale && pageLocales[nuxtI18nConfig.defaultLocale] && pageLocales[nuxtI18nConfig.defaultLocale] !== false)
757
748
  alternatives.push({ hreflang: "x-default", href: generatePathForI18nPages({ normalisedLocales, localeCode: nuxtI18nConfig.defaultLocale, pageLocales: pageLocales[nuxtI18nConfig.defaultLocale], nuxtI18nConfig }) });
758
749
  i18nPagesSources.urls.push({
759
750
  _sitemap: locale._sitemap,
@@ -783,7 +774,9 @@ const module = defineNuxtModule({
783
774
  differentDomains: nuxtI18nConfig.differentDomains,
784
775
  defaultLocale: nuxtI18nConfig.defaultLocale,
785
776
  locales: normalisedLocales,
786
- strategy: nuxtI18nConfig.strategy
777
+ strategy: nuxtI18nConfig.strategy,
778
+ // @ts-expect-error untyped
779
+ pages: nuxtI18nConfig.pages
787
780
  };
788
781
  }
789
782
  let canI18nMap = config.sitemaps !== false && nuxtI18nConfig.strategy !== "no_prefix";
@@ -840,6 +833,7 @@ declare module 'nitropack' {
840
833
  'sitemap:input': (ctx: import('${typesPath}').SitemapInputCtx) => void | Promise<void>
841
834
  'sitemap:resolved': (ctx: import('${typesPath}').SitemapRenderCtx) => void | Promise<void>
842
835
  'sitemap:output': (ctx: import('${typesPath}').SitemapOutputHookCtx) => void | Promise<void>
836
+ 'sitemap:sources': (ctx: import('${typesPath}').SitemapSourcesHookCtx) => void | Promise<void>
843
837
  }
844
838
  }
845
839
  declare module 'vue-router' {
@@ -849,7 +843,6 @@ declare module 'vue-router' {
849
843
  }
850
844
  `;
851
845
  });
852
- const nitroPreset = resolveNitroPreset();
853
846
  const prerenderedRoutes = nuxt.options.nitro.prerender?.routes || [];
854
847
  const prerenderSitemap = isNuxtGenerate() || includesSitemapRoot(config.sitemapName, prerenderedRoutes);
855
848
  const routeRules = {};
@@ -861,17 +854,6 @@ declare module 'vue-router' {
861
854
  "X-Sitemap-Prerendered": (/* @__PURE__ */ new Date()).toISOString()
862
855
  };
863
856
  }
864
- if (!nuxt.options.dev && !isNuxtGenerate() && config.cacheMaxAgeSeconds && config.runtimeCacheStorage !== false) {
865
- routeRules[nitroPreset.includes("vercel") ? "isr" : "swr"] = config.cacheMaxAgeSeconds;
866
- routeRules.cache = {
867
- // handle multi-tenancy
868
- swr: true,
869
- maxAge: config.cacheMaxAgeSeconds,
870
- varies: ["X-Forwarded-Host", "X-Forwarded-Proto", "Host"]
871
- };
872
- if (typeof config.runtimeCacheStorage === "object")
873
- routeRules.cache.base = "sitemap";
874
- }
875
857
  if (config.xsl) {
876
858
  nuxt.options.nitro.routeRules[config.xsl] = {
877
859
  headers: {
@@ -884,10 +866,16 @@ declare module 'vue-router' {
884
866
  nuxt.options.nitro.routeRules["/sitemap_index.xml"] = routeRules;
885
867
  if (typeof config.sitemaps === "object") {
886
868
  for (const k in config.sitemaps) {
869
+ if (k === "index")
870
+ continue;
887
871
  nuxt.options.nitro.routeRules[joinURL(config.sitemapsPathPrefix || "", `/${k}.xml`)] = routeRules;
872
+ const sitemapConfig = config.sitemaps[k];
873
+ if (sitemapConfig.chunks) {
874
+ nuxt.options.nitro.routeRules[joinURL(config.sitemapsPathPrefix || "", `/${k}-*.xml`)] = routeRules;
875
+ }
888
876
  }
889
877
  } else {
890
- nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
878
+ nuxt.options.nitro.routeRules[joinURL(config.sitemapsPathPrefix || "", `/[0-9]+.xml`)] = routeRules;
891
879
  }
892
880
  } else {
893
881
  nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
@@ -905,14 +893,25 @@ declare module 'vue-router' {
905
893
  if (nuxt.options._installedModules.some((m) => m.meta.name === "Content")) {
906
894
  logger.warn("You have loaded `@nuxt/content` before `@nuxtjs/sitemap`, this may cause issues with the integration. Please ensure `@nuxtjs/sitemap` is loaded first.");
907
895
  }
896
+ config.exclude.push("/__nuxt_content/**");
908
897
  nuxt.options.alias["#sitemap/content-v3-nitro-path"] = resolve(dirname(resolveModule("@nuxt/content")), "runtime/nitro");
909
898
  nuxt.hooks.hook("content:file:afterParse", (ctx) => {
910
899
  const content = ctx.content;
911
900
  nuxtV3Collections.add(ctx.collection.name);
912
- if (ctx.file.path.endsWith("/.navigation")) {
901
+ if (String(ctx.content.path).includes("/.")) {
902
+ ctx.content.sitemap = null;
913
903
  return;
914
904
  }
915
905
  if (!("sitemap" in ctx.collection.fields)) {
906
+ ctx.content.sitemap = null;
907
+ return;
908
+ }
909
+ if (typeof content.sitemap !== "undefined" && !content.sitemap) {
910
+ ctx.content.sitemap = null;
911
+ return;
912
+ }
913
+ if (ctx.content.robots === false) {
914
+ ctx.content.sitemap = null;
916
915
  return;
917
916
  }
918
917
  const images = [];
@@ -986,13 +985,25 @@ declare module 'vue-router' {
986
985
  middleware: false
987
986
  });
988
987
  } else {
989
- for (const sitemapName of Object.keys(config.sitemaps || {})) {
988
+ const sitemapNames = Object.keys(config.sitemaps || {});
989
+ for (const sitemapName of sitemapNames) {
990
+ if (sitemapName === "index")
991
+ continue;
992
+ const sitemapConfig = config.sitemaps[sitemapName];
990
993
  addServerHandler({
991
994
  route: withLeadingSlash(`${sitemapName}.xml`),
992
995
  handler: resolve("./runtime/server/routes/sitemap/[sitemap].xml"),
993
996
  lazy: true,
994
997
  middleware: false
995
998
  });
999
+ if (sitemapConfig.chunks) {
1000
+ addServerHandler({
1001
+ route: `/${sitemapName}-*.xml`,
1002
+ handler: resolve("./runtime/server/routes/sitemap/[sitemap].xml"),
1003
+ lazy: true,
1004
+ middleware: false
1005
+ });
1006
+ }
996
1007
  }
997
1008
  }
998
1009
  sitemaps.index = {
@@ -1006,7 +1017,7 @@ declare module 'vue-router' {
1006
1017
  if (sitemapName === "index")
1007
1018
  continue;
1008
1019
  const definition = config.sitemaps[sitemapName];
1009
- sitemaps[sitemapName] = defu(
1020
+ const sitemapConfig = defu(
1010
1021
  {
1011
1022
  sitemapName,
1012
1023
  _route: withBase(joinURL(config.sitemapsPathPrefix || "", `${sitemapName}.xml`), nuxt.options.app.baseURL || "/"),
@@ -1015,6 +1026,28 @@ declare module 'vue-router' {
1015
1026
  { ...definition, urls: void 0, sources: void 0 },
1016
1027
  { include: config.include, exclude: config.exclude }
1017
1028
  );
1029
+ if (definition.chunks) {
1030
+ let chunkSize = config.defaultSitemapsChunkSize || 1e3;
1031
+ if (typeof definition.chunks === "number") {
1032
+ if (definition.chunks <= 0) {
1033
+ logger.warn(`Invalid chunks value (${definition.chunks}) for sitemap "${sitemapName}". Using default.`);
1034
+ } else {
1035
+ chunkSize = definition.chunks;
1036
+ }
1037
+ }
1038
+ if (definition.chunkSize !== void 0) {
1039
+ if (typeof definition.chunkSize !== "number" || definition.chunkSize <= 0) {
1040
+ logger.warn(`Invalid chunkSize value (${definition.chunkSize}) for sitemap "${sitemapName}". Using default.`);
1041
+ } else {
1042
+ chunkSize = definition.chunkSize;
1043
+ }
1044
+ }
1045
+ sitemapConfig._isChunking = true;
1046
+ sitemapConfig._chunkSize = chunkSize;
1047
+ sitemapConfig.chunks = definition.chunks;
1048
+ sitemapConfig.chunkSize = definition.chunkSize;
1049
+ }
1050
+ sitemaps[sitemapName] = sitemapConfig;
1018
1051
  }
1019
1052
  } else {
1020
1053
  sitemaps.chunks = {
@@ -1043,7 +1076,7 @@ declare module 'vue-router' {
1043
1076
  if (typeof path !== "string")
1044
1077
  return [path];
1045
1078
  const withoutSlashes = withoutTrailingSlash(withoutLeadingSlash(path)).replace("/index", "");
1046
- if (withoutSlashes in pages) {
1079
+ if (pages && withoutSlashes in pages) {
1047
1080
  const pageLocales = pages[withoutSlashes];
1048
1081
  if (pageLocales) {
1049
1082
  return Object.keys(pageLocales).map((localeCode) => withLeadingSlash(generatePathForI18nPages({
@@ -1056,7 +1089,7 @@ declare module 'vue-router' {
1056
1089
  }
1057
1090
  let match = [path];
1058
1091
  Object.values(pages).forEach((pageLocales) => {
1059
- if (nuxtI18nConfig.defaultLocale in pageLocales && pageLocales[nuxtI18nConfig.defaultLocale] === path)
1092
+ if (pageLocales && nuxtI18nConfig.defaultLocale in pageLocales && pageLocales[nuxtI18nConfig.defaultLocale] === path)
1060
1093
  match = Object.keys(pageLocales).map((localeCode) => withLeadingSlash(generatePathForI18nPages({ localeCode, pageLocales: pageLocales[localeCode], nuxtI18nConfig, normalisedLocales })));
1061
1094
  });
1062
1095
  return match;
@@ -1116,6 +1149,14 @@ declare module 'vue-router' {
1116
1149
  route: "/__sitemap__/debug.json",
1117
1150
  handler: resolve("./runtime/server/routes/__sitemap__/debug")
1118
1151
  });
1152
+ if (usingMultiSitemaps) {
1153
+ addServerHandler({
1154
+ route: "/__sitemap__/**:sitemap",
1155
+ handler: resolve("./runtime/server/routes/sitemap/[sitemap].xml"),
1156
+ lazy: true,
1157
+ middleware: true
1158
+ });
1159
+ }
1119
1160
  setupDevToolsUI(config, resolve);
1120
1161
  }
1121
1162
  const imports = [
@@ -4,20 +4,36 @@ import { useSitemapRuntimeConfig } from "../utils.js";
4
4
  export default defineNitroPlugin((nitroApp) => {
5
5
  const { sitemaps } = useSitemapRuntimeConfig();
6
6
  const queue = [];
7
+ const timeoutIds = [];
7
8
  const sitemapsWithRoutes = Object.entries(sitemaps).filter(([, sitemap]) => sitemap._route);
8
9
  for (const [, sitemap] of sitemapsWithRoutes)
9
10
  queue.push(() => nitroApp.localFetch(withLeadingSlash(sitemap._route), {}));
10
- setTimeout(
11
+ const initialTimeout = setTimeout(
11
12
  () => {
12
13
  const next = async () => {
13
- if (queue.length === 0)
14
+ if (queue.length === 0) {
15
+ timeoutIds.length = 0;
14
16
  return;
15
- await queue.shift()();
16
- setTimeout(next, 1e3);
17
+ }
18
+ try {
19
+ await queue.shift()();
20
+ } catch (error) {
21
+ console.error("[sitemap:warm-up] Error warming up sitemap:", error);
22
+ }
23
+ if (queue.length > 0) {
24
+ const nextTimeout = setTimeout(next, 1e3);
25
+ timeoutIds.push(nextTimeout);
26
+ }
17
27
  };
18
28
  next();
19
29
  },
20
30
  2500
21
31
  /* https://github.com/unjs/nitro/pull/1906 */
22
32
  );
33
+ timeoutIds.push(initialTimeout);
34
+ nitroApp.hooks.hook("close", () => {
35
+ timeoutIds.forEach((id) => clearTimeout(id));
36
+ timeoutIds.length = 0;
37
+ queue.length = 0;
38
+ });
23
39
  });
@@ -7,18 +7,18 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
7
7
  isNuxtContentDocumentDriven: boolean;
8
8
  sitemaps: {
9
9
  index?: Pick<SitemapDefinition, "sitemapName" | "_route"> & {
10
- sitemaps: import("../../../types").SitemapIndexEntry[];
10
+ sitemaps: import("../../../types.js").SitemapIndexEntry[];
11
11
  };
12
12
  } & Record<string, Omit<SitemapDefinition, "urls"> & {
13
13
  _hasSourceChunk?: boolean;
14
14
  }>;
15
- autoI18n?: import("../../../types").AutoI18nConfig;
15
+ autoI18n?: import("../../../types.js").AutoI18nConfig;
16
16
  isMultiSitemap: boolean;
17
17
  isI18nMapped: boolean;
18
18
  sitemapsPathPrefix: string | false;
19
19
  cacheMaxAgeSeconds: number | false;
20
20
  sitemapName: string;
21
- excludeAppSources: true | (import("../../../types").AppSourceContext[]);
21
+ excludeAppSources: true | (import("../../../types.js").AppSourceContext[]);
22
22
  sortEntries: boolean;
23
23
  defaultSitemapsChunkSize: number | false;
24
24
  xslColumns?: {
@@ -35,6 +35,6 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
35
35
  credits: boolean;
36
36
  minify: boolean;
37
37
  };
38
- globalSources: import("../../../types").SitemapSourceResolved[];
38
+ globalSources: import("../../../types.js").SitemapSourceResolved[];
39
39
  }>>;
40
40
  export default _default;
@@ -17,13 +17,13 @@ export default defineEventHandler(async (e) => {
17
17
  for (const s of Object.keys(_sitemaps)) {
18
18
  sitemaps[s] = {
19
19
  ..._sitemaps[s],
20
- sources: await resolveSitemapSources(await childSitemapSources(_sitemaps[s]))
20
+ sources: await resolveSitemapSources(await childSitemapSources(_sitemaps[s]), e)
21
21
  };
22
22
  }
23
23
  return {
24
24
  nitroOrigin,
25
25
  sitemaps,
26
26
  runtimeConfig,
27
- globalSources: await resolveSitemapSources(globalSources)
27
+ globalSources: await resolveSitemapSources(globalSources, e)
28
28
  };
29
29
  });
@@ -10,8 +10,13 @@ export default defineEventHandler(async (e) => {
10
10
  }
11
11
  const contentList = [];
12
12
  for (const collection of collections) {
13
- contentList.push(queryCollectionWithEvent(e, collection).select("path", "sitemap").where("path", "IS NOT NULL").all());
13
+ contentList.push(queryCollectionWithEvent(e, collection).select("path", "sitemap").where("path", "IS NOT NULL").where("sitemap", "IS NOT NULL").all());
14
14
  }
15
15
  const results = await Promise.all(contentList);
16
- return results.flat().map((c) => ({ loc: c.path, ...c.sitemap })).filter(Boolean);
16
+ return results.flatMap((c) => {
17
+ return c.filter((c2) => c2.sitemap !== false && c2.path).flatMap((c2) => ({
18
+ loc: c2.path,
19
+ ...c2.sitemap || {}
20
+ }));
21
+ }).filter(Boolean);
17
22
  });
@@ -2,19 +2,49 @@ import { createError, defineEventHandler, getRouterParam } from "h3";
2
2
  import { withoutLeadingSlash, withoutTrailingSlash } from "ufo";
3
3
  import { useSitemapRuntimeConfig } from "../../utils.js";
4
4
  import { createSitemap } from "../../sitemap/nitro.js";
5
+ import { parseChunkInfo, getSitemapConfig } from "../../sitemap/utils/chunk.js";
5
6
  export default defineEventHandler(async (e) => {
6
7
  const runtimeConfig = useSitemapRuntimeConfig(e);
7
8
  const { sitemaps } = runtimeConfig;
8
- const sitemapName = withoutLeadingSlash(withoutTrailingSlash((getRouterParam(e, "sitemap") || e.path)?.replace(".xml", "").replace(runtimeConfig.sitemapsPathPrefix || "", "")));
9
- const isChunking = typeof sitemaps.chunks !== "undefined" && !Number.isNaN(Number(sitemapName));
10
- if (!sitemapName || !(sitemapName in sitemaps) && !isChunking) {
9
+ let sitemapName = getRouterParam(e, "sitemap");
10
+ if (!sitemapName) {
11
+ const path = e.path;
12
+ const match = path.match(/(?:\/__sitemap__\/)?([^/]+)\.xml$/);
13
+ if (match) {
14
+ sitemapName = match[1];
15
+ }
16
+ }
17
+ if (!sitemapName) {
18
+ return createError({
19
+ statusCode: 400,
20
+ message: "Invalid sitemap request"
21
+ });
22
+ }
23
+ sitemapName = withoutLeadingSlash(withoutTrailingSlash(sitemapName.replace(".xml", "").replace("__sitemap__/", "").replace(runtimeConfig.sitemapsPathPrefix || "", "")));
24
+ const chunkInfo = parseChunkInfo(sitemapName, sitemaps, runtimeConfig.defaultSitemapsChunkSize);
25
+ const isAutoChunked = typeof sitemaps.chunks !== "undefined" && !Number.isNaN(Number(sitemapName));
26
+ const sitemapExists = sitemapName in sitemaps || chunkInfo.baseSitemapName in sitemaps || isAutoChunked;
27
+ if (!sitemapExists) {
11
28
  return createError({
12
29
  statusCode: 404,
13
30
  message: `Sitemap "${sitemapName}" not found.`
14
31
  });
15
32
  }
16
- return createSitemap(e, isChunking ? {
17
- ...sitemaps.chunks,
18
- sitemapName
19
- } : sitemaps[sitemapName], runtimeConfig);
33
+ if (chunkInfo.isChunked && chunkInfo.chunkIndex !== void 0) {
34
+ const baseSitemap = sitemaps[chunkInfo.baseSitemapName];
35
+ if (baseSitemap && !baseSitemap.chunks && !baseSitemap._isChunking) {
36
+ return createError({
37
+ statusCode: 404,
38
+ message: `Sitemap "${chunkInfo.baseSitemapName}" does not support chunking.`
39
+ });
40
+ }
41
+ if (baseSitemap?._chunkCount !== void 0 && chunkInfo.chunkIndex >= baseSitemap._chunkCount) {
42
+ return createError({
43
+ statusCode: 404,
44
+ message: `Chunk ${chunkInfo.chunkIndex} does not exist for sitemap "${chunkInfo.baseSitemapName}".`
45
+ });
46
+ }
47
+ }
48
+ const sitemapConfig = getSitemapConfig(sitemapName, sitemaps, runtimeConfig.defaultSitemapsChunkSize);
49
+ return createSitemap(e, sitemapConfig, runtimeConfig);
20
50
  });
@@ -22,9 +22,17 @@ export default defineEventHandler(async (e) => {
22
22
  const ctx = { sitemap: output, sitemapName: "sitemap", event: e };
23
23
  await nitro.hooks.callHook("sitemap:output", ctx);
24
24
  setHeader(e, "Content-Type", "text/xml; charset=UTF-8");
25
- if (runtimeConfig.cacheMaxAgeSeconds)
26
- setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, must-revalidate`);
27
- else
25
+ if (runtimeConfig.cacheMaxAgeSeconds) {
26
+ setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
27
+ const now = /* @__PURE__ */ new Date();
28
+ setHeader(e, "X-Sitemap-Generated", now.toISOString());
29
+ setHeader(e, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
30
+ const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
31
+ setHeader(e, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
32
+ const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
33
+ setHeader(e, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
34
+ } else {
28
35
  setHeader(e, "Cache-Control", `no-cache, no-store`);
36
+ }
29
37
  return ctx.sitemap;
30
38
  });
@@ -1,4 +1,4 @@
1
1
  import type { NitroApp } from 'nitropack/types';
2
2
  import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapIndexEntry } from '../../../types.js';
3
- export declare function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<SitemapIndexEntry[]>;
4
3
  export declare function urlsToIndexXml(sitemaps: SitemapIndexEntry[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>): string;
4
+ export declare function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<SitemapIndexEntry[]>;
@@ -1,11 +1,33 @@
1
1
  import { defu } from "defu";
2
2
  import { joinURL } from "ufo";
3
+ import { defineCachedFunction } from "nitropack/runtime";
4
+ import { getHeader } from "h3";
3
5
  import { normaliseDate } from "../urlset/normalise.js";
4
- import { globalSitemapSources, resolveSitemapSources } from "../urlset/sources.js";
5
- import { sortSitemapUrls } from "../urlset/sort.js";
6
- import { escapeValueForXml, wrapSitemapXml } from "./xml.js";
6
+ import { globalSitemapSources, childSitemapSources, resolveSitemapSources } from "../urlset/sources.js";
7
+ import { sortInPlace } from "../urlset/sort.js";
8
+ import { escapeValueForXml } from "./xml.js";
7
9
  import { resolveSitemapEntries } from "./sitemap.js";
8
- export async function buildSitemapIndex(resolvers, runtimeConfig, nitro) {
10
+ const buildSitemapIndexCached = defineCachedFunction(
11
+ async (event, resolvers, runtimeConfig, nitro) => {
12
+ return buildSitemapIndexInternal(resolvers, runtimeConfig, nitro);
13
+ },
14
+ {
15
+ name: "sitemap:index",
16
+ group: "sitemap",
17
+ maxAge: 60 * 10,
18
+ // 10 minutes default
19
+ base: "sitemap",
20
+ // Use the sitemap storage
21
+ getKey: (event) => {
22
+ const host = getHeader(event, "host") || getHeader(event, "x-forwarded-host") || "";
23
+ const proto = getHeader(event, "x-forwarded-proto") || "https";
24
+ return `sitemap-index-${proto}-${host}`;
25
+ },
26
+ swr: true
27
+ // Enable stale-while-revalidate
28
+ }
29
+ );
30
+ async function buildSitemapIndexInternal(resolvers, runtimeConfig, nitro) {
9
31
  const {
10
32
  sitemaps,
11
33
  // enhancing
@@ -20,13 +42,32 @@ export async function buildSitemapIndex(resolvers, runtimeConfig, nitro) {
20
42
  if (!sitemaps)
21
43
  throw new Error("Attempting to build a sitemap index without required `sitemaps` configuration.");
22
44
  function maybeSort(urls) {
23
- return sortEntries ? sortSitemapUrls(urls) : urls;
45
+ return sortEntries ? sortInPlace(urls) : urls;
24
46
  }
25
- const isChunking = typeof sitemaps.chunks !== "undefined";
26
47
  const chunks = {};
27
- if (isChunking) {
48
+ for (const sitemapName in sitemaps) {
49
+ if (sitemapName === "index" || sitemapName === "chunks") continue;
50
+ const sitemapConfig = sitemaps[sitemapName];
51
+ if (sitemapConfig.chunks || sitemapConfig._isChunking) {
52
+ sitemapConfig._isChunking = true;
53
+ sitemapConfig._chunkSize = typeof sitemapConfig.chunks === "number" ? sitemapConfig.chunks : sitemapConfig.chunkSize || defaultSitemapsChunkSize || 1e3;
54
+ } else {
55
+ chunks[sitemapName] = chunks[sitemapName] || { urls: [] };
56
+ }
57
+ }
58
+ if (typeof sitemaps.chunks !== "undefined") {
28
59
  const sitemap = sitemaps.chunks;
29
- const sources = await resolveSitemapSources(await globalSitemapSources());
60
+ let sourcesInput = await globalSitemapSources();
61
+ if (nitro && resolvers.event) {
62
+ const ctx = {
63
+ event: resolvers.event,
64
+ sitemapName: sitemap.sitemapName,
65
+ sources: sourcesInput
66
+ };
67
+ await nitro.hooks.callHook("sitemap:sources", ctx);
68
+ sourcesInput = ctx.sources;
69
+ }
70
+ const sources = await resolveSitemapSources(sourcesInput, resolvers.event);
30
71
  const resolvedCtx = {
31
72
  urls: sources.flatMap((s) => s.urls),
32
73
  sitemapName: sitemap.sitemapName,
@@ -41,12 +82,6 @@ export async function buildSitemapIndex(resolvers, runtimeConfig, nitro) {
41
82
  chunks[chunkIndex] = chunks[chunkIndex] || { urls: [] };
42
83
  chunks[chunkIndex].urls.push(url);
43
84
  });
44
- } else {
45
- for (const sitemap in sitemaps) {
46
- if (sitemap !== "index") {
47
- chunks[sitemap] = chunks[sitemap] || { urls: [] };
48
- }
49
- }
50
85
  }
51
86
  const entries = [];
52
87
  for (const name in chunks) {
@@ -62,6 +97,48 @@ export async function buildSitemapIndex(resolvers, runtimeConfig, nitro) {
62
97
  entry.lastmod = normaliseDate(lastmod);
63
98
  entries.push(entry);
64
99
  }
100
+ for (const sitemapName in sitemaps) {
101
+ if (sitemapName !== "index" && sitemaps[sitemapName]._isChunking) {
102
+ const sitemapConfig = sitemaps[sitemapName];
103
+ const chunkSize = sitemapConfig._chunkSize || defaultSitemapsChunkSize || 1e3;
104
+ let sourcesInput = sitemapConfig.includeAppSources ? await globalSitemapSources() : [];
105
+ sourcesInput.push(...await childSitemapSources(sitemapConfig));
106
+ if (nitro && resolvers.event) {
107
+ const ctx = {
108
+ event: resolvers.event,
109
+ sitemapName: sitemapConfig.sitemapName,
110
+ sources: sourcesInput
111
+ };
112
+ await nitro.hooks.callHook("sitemap:sources", ctx);
113
+ sourcesInput = ctx.sources;
114
+ }
115
+ const sources = await resolveSitemapSources(sourcesInput, resolvers.event);
116
+ const resolvedCtx = {
117
+ urls: sources.flatMap((s) => s.urls),
118
+ sitemapName: sitemapConfig.sitemapName,
119
+ event: resolvers.event
120
+ };
121
+ await nitro?.hooks.callHook("sitemap:input", resolvedCtx);
122
+ const normalisedUrls = resolveSitemapEntries(sitemapConfig, resolvedCtx.urls, { autoI18n, isI18nMapped }, resolvers);
123
+ const totalUrls = normalisedUrls.length;
124
+ const chunkCount = Math.ceil(totalUrls / chunkSize);
125
+ sitemapConfig._chunkCount = chunkCount;
126
+ for (let i = 0; i < chunkCount; i++) {
127
+ const chunkName = `${sitemapName}-${i}`;
128
+ const entry = {
129
+ _sitemapName: chunkName,
130
+ sitemap: resolvers.canonicalUrlResolver(joinURL(sitemapsPathPrefix || "", `/${chunkName}.xml`))
131
+ };
132
+ const chunkUrls = normalisedUrls.slice(i * chunkSize, (i + 1) * chunkSize);
133
+ let lastmod = chunkUrls.filter((a) => !!a?.lastmod).map((a) => typeof a.lastmod === "string" ? new Date(a.lastmod) : a.lastmod).sort((a, b) => (b?.getTime() || 0) - (a?.getTime() || 0))?.[0];
134
+ if (!lastmod && autoLastmod)
135
+ lastmod = /* @__PURE__ */ new Date();
136
+ if (lastmod)
137
+ entry.lastmod = normaliseDate(lastmod);
138
+ entries.push(entry);
139
+ }
140
+ }
141
+ }
65
142
  if (sitemaps.index) {
66
143
  entries.push(...sitemaps.index.sitemaps.map((entry) => {
67
144
  return typeof entry === "string" ? { sitemap: entry } : entry;
@@ -77,9 +154,26 @@ export function urlsToIndexXml(sitemaps, resolvers, { version, xsl, credits, min
77
154
  e.lastmod ? ` <lastmod>${escapeValueForXml(e.lastmod)}</lastmod>` : false,
78
155
  " </sitemap>"
79
156
  ].filter(Boolean).join("\n")).join("\n");
80
- return wrapSitemapXml([
157
+ const xmlParts = [
158
+ '<?xml version="1.0" encoding="UTF-8"?>'
159
+ ];
160
+ if (xsl) {
161
+ const relativeBaseUrl = resolvers.relativeBaseUrlResolver?.(xsl) ?? xsl;
162
+ xmlParts.push(`<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`);
163
+ }
164
+ xmlParts.push(
81
165
  '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
82
166
  sitemapXml,
83
167
  "</sitemapindex>"
84
- ], resolvers, { version, xsl, credits, minify });
168
+ );
169
+ if (credits) {
170
+ xmlParts.push(`<!-- XML Sitemap Index generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`);
171
+ }
172
+ return minify ? xmlParts.join("").replace(/(?<!<[^>]*)\s(?![^<]*>)/g, "") : xmlParts.join("\n");
173
+ }
174
+ export async function buildSitemapIndex(resolvers, runtimeConfig, nitro) {
175
+ if (!import.meta.dev && !!runtimeConfig.cacheMaxAgeSeconds && runtimeConfig.cacheMaxAgeSeconds > 0 && resolvers.event) {
176
+ return buildSitemapIndexCached(resolvers.event, resolvers, runtimeConfig, nitro);
177
+ }
178
+ return buildSitemapIndexInternal(resolvers, runtimeConfig, nitro);
85
179
  }
@@ -7,4 +7,4 @@ export interface NormalizedI18n extends ResolvedSitemapUrl {
7
7
  }
8
8
  export declare function resolveSitemapEntries(sitemap: SitemapDefinition, urls: SitemapUrlInput[], runtimeConfig: Pick<ModuleRuntimeConfig, 'autoI18n' | 'isI18nMapped'>, resolvers?: NitroUrlResolvers): ResolvedSitemapUrl[];
9
9
  export declare function buildSitemapUrls(sitemap: SitemapDefinition, resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<ResolvedSitemapUrl[]>;
10
- export declare function urlsToXml(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>): string;
10
+ export { urlsToXml } from './xml.js';