@nuxtjs/sitemap 7.2.10 → 7.3.1

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 (40) hide show
  1. package/dist/client/200.html +10 -9
  2. package/dist/client/404.html +10 -9
  3. package/dist/client/_nuxt/{DDzCRRw4.js → 5vafBU9X.js} +1 -1
  4. package/dist/client/_nuxt/BIHI7g3E.js +1 -0
  5. package/dist/client/_nuxt/Bn78IMkz.js +172 -0
  6. package/dist/client/_nuxt/{DdvkoY3I.js → CT3BV8Rj.js} +1 -1
  7. package/dist/client/_nuxt/Cp-IABpG.js +1 -0
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/5ecca6e1-2b8a-4fc5-a128-35cbc27bf6d7.json +1 -0
  10. package/dist/client/_nuxt/{entry.BrROiVtQ.css → entry.BzbtAPc0.css} +1 -1
  11. package/dist/client/_nuxt/error-404.D_zhMyJm.css +1 -0
  12. package/dist/client/_nuxt/error-500.rdOYVbxo.css +1 -0
  13. package/dist/client/index.html +10 -9
  14. package/dist/module.cjs +59 -20
  15. package/dist/module.json +2 -2
  16. package/dist/module.mjs +60 -21
  17. package/dist/runtime/server/plugins/warm-up.js +20 -4
  18. package/dist/runtime/server/routes/__sitemap__/debug.js +2 -2
  19. package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +37 -7
  20. package/dist/runtime/server/routes/sitemap_index.xml.js +11 -3
  21. package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +1 -1
  22. package/dist/runtime/server/sitemap/builder/sitemap-index.js +110 -16
  23. package/dist/runtime/server/sitemap/builder/sitemap.d.ts +1 -1
  24. package/dist/runtime/server/sitemap/builder/sitemap.js +62 -36
  25. package/dist/runtime/server/sitemap/builder/xml.d.ts +2 -3
  26. package/dist/runtime/server/sitemap/builder/xml.js +182 -80
  27. package/dist/runtime/server/sitemap/nitro.js +68 -20
  28. package/dist/runtime/server/sitemap/urlset/normalise.js +21 -19
  29. package/dist/runtime/server/sitemap/urlset/sort.d.ts +1 -1
  30. package/dist/runtime/server/sitemap/urlset/sort.js +9 -15
  31. package/dist/runtime/server/sitemap/utils/chunk.d.ts +10 -0
  32. package/dist/runtime/server/sitemap/utils/chunk.js +66 -0
  33. package/dist/runtime/types.d.ts +44 -0
  34. package/dist/runtime/utils-pure.js +13 -5
  35. package/package.json +23 -22
  36. package/dist/client/_nuxt/BLmTiKMJ.js +0 -1
  37. package/dist/client/_nuxt/BUP090M8.js +0 -172
  38. package/dist/client/_nuxt/builds/meta/3c351607-eab3-459a-b743-aba04e49a80e.json +0 -1
  39. package/dist/client/_nuxt/error-404.BMkETmdU.css +0 -1
  40. package/dist/client/_nuxt/error-500.C3_I-O7u.css +0 -1
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { useNuxt, loadNuxtModuleInstance, createResolver, addTemplate, tryUseNuxt, extendPages, defineNuxtModule, useLogger, hasNuxtModule, getNuxtModuleVersion, hasNuxtModuleCompatibility, addServerImports, addServerPlugin, resolveModule, addServerHandler, addPrerenderRoutes } from '@nuxt/kit';
1
+ import { useNuxt, loadNuxtModuleInstance, createResolver, addTemplate, extendPages, tryUseNuxt, defineNuxtModule, useLogger, hasNuxtModule, getNuxtModuleVersion, hasNuxtModuleCompatibility, addServerImports, addServerPlugin, resolveModule, addServerHandler, addPrerenderRoutes } from '@nuxt/kit';
2
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';
@@ -450,9 +450,9 @@ function setupPrerenderHandler(_options, nuxt = useNuxt()) {
450
450
  }), route._sitemap);
451
451
  });
452
452
  nitro.hooks.hook("prerender:done", async () => {
453
- const isNuxt4 = nuxt.options._majorVersion === 4;
453
+ const isNuxt5 = nuxt.options._majorVersion === 5;
454
454
  let nitroModule;
455
- if (isNuxt4) {
455
+ if (isNuxt5) {
456
456
  nitroModule = await import(String("nitro"));
457
457
  } else {
458
458
  nitroModule = await import(String("nitropack"));
@@ -740,11 +740,11 @@ const module = defineNuxtModule({
740
740
  const locale = normalisedLocales.find((l) => l.code === localeCode);
741
741
  if (!locale || !pageLocales[localeCode] || pageLocales[localeCode].includes("["))
742
742
  continue;
743
- const alternatives = Object.keys(pageLocales).map((l) => ({
743
+ const alternatives = Object.keys(pageLocales).filter((l) => pageLocales[l] !== false).map((l) => ({
744
744
  hreflang: normalisedLocales.find((nl) => nl.code === l)?._hreflang || l,
745
745
  href: generatePathForI18nPages({ localeCode: l, pageLocales: pageLocales[l], nuxtI18nConfig, normalisedLocales })
746
746
  }));
747
- if (alternatives.length && nuxtI18nConfig.defaultLocale && pageLocales[nuxtI18nConfig.defaultLocale])
747
+ if (alternatives.length && nuxtI18nConfig.defaultLocale && pageLocales[nuxtI18nConfig.defaultLocale] && pageLocales[nuxtI18nConfig.defaultLocale] !== false)
748
748
  alternatives.push({ hreflang: "x-default", href: generatePathForI18nPages({ normalisedLocales, localeCode: nuxtI18nConfig.defaultLocale, pageLocales: pageLocales[nuxtI18nConfig.defaultLocale], nuxtI18nConfig }) });
749
749
  i18nPagesSources.urls.push({
750
750
  _sitemap: locale._sitemap,
@@ -774,7 +774,9 @@ const module = defineNuxtModule({
774
774
  differentDomains: nuxtI18nConfig.differentDomains,
775
775
  defaultLocale: nuxtI18nConfig.defaultLocale,
776
776
  locales: normalisedLocales,
777
- strategy: nuxtI18nConfig.strategy
777
+ strategy: nuxtI18nConfig.strategy,
778
+ // @ts-expect-error untyped
779
+ pages: nuxtI18nConfig.pages
778
780
  };
779
781
  }
780
782
  let canI18nMap = config.sitemaps !== false && nuxtI18nConfig.strategy !== "no_prefix";
@@ -831,6 +833,7 @@ declare module 'nitropack' {
831
833
  'sitemap:input': (ctx: import('${typesPath}').SitemapInputCtx) => void | Promise<void>
832
834
  'sitemap:resolved': (ctx: import('${typesPath}').SitemapRenderCtx) => void | Promise<void>
833
835
  'sitemap:output': (ctx: import('${typesPath}').SitemapOutputHookCtx) => void | Promise<void>
836
+ 'sitemap:sources': (ctx: import('${typesPath}').SitemapSourcesHookCtx) => void | Promise<void>
834
837
  }
835
838
  }
836
839
  declare module 'vue-router' {
@@ -840,7 +843,6 @@ declare module 'vue-router' {
840
843
  }
841
844
  `;
842
845
  });
843
- const nitroPreset = resolveNitroPreset();
844
846
  const prerenderedRoutes = nuxt.options.nitro.prerender?.routes || [];
845
847
  const prerenderSitemap = isNuxtGenerate() || includesSitemapRoot(config.sitemapName, prerenderedRoutes);
846
848
  const routeRules = {};
@@ -852,17 +854,6 @@ declare module 'vue-router' {
852
854
  "X-Sitemap-Prerendered": (/* @__PURE__ */ new Date()).toISOString()
853
855
  };
854
856
  }
855
- if (!nuxt.options.dev && !isNuxtGenerate() && config.cacheMaxAgeSeconds && config.runtimeCacheStorage !== false) {
856
- routeRules[nitroPreset.includes("vercel") ? "isr" : "swr"] = config.cacheMaxAgeSeconds;
857
- routeRules.cache = {
858
- // handle multi-tenancy
859
- swr: true,
860
- maxAge: config.cacheMaxAgeSeconds,
861
- varies: ["X-Forwarded-Host", "X-Forwarded-Proto", "Host"]
862
- };
863
- if (typeof config.runtimeCacheStorage === "object")
864
- routeRules.cache.base = "sitemap";
865
- }
866
857
  if (config.xsl) {
867
858
  nuxt.options.nitro.routeRules[config.xsl] = {
868
859
  headers: {
@@ -875,10 +866,16 @@ declare module 'vue-router' {
875
866
  nuxt.options.nitro.routeRules["/sitemap_index.xml"] = routeRules;
876
867
  if (typeof config.sitemaps === "object") {
877
868
  for (const k in config.sitemaps) {
869
+ if (k === "index")
870
+ continue;
878
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
+ }
879
876
  }
880
877
  } else {
881
- nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
878
+ nuxt.options.nitro.routeRules[joinURL(config.sitemapsPathPrefix || "", `/[0-9]+.xml`)] = routeRules;
882
879
  }
883
880
  } else {
884
881
  nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
@@ -988,13 +985,25 @@ declare module 'vue-router' {
988
985
  middleware: false
989
986
  });
990
987
  } else {
991
- 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];
992
993
  addServerHandler({
993
994
  route: withLeadingSlash(`${sitemapName}.xml`),
994
995
  handler: resolve("./runtime/server/routes/sitemap/[sitemap].xml"),
995
996
  lazy: true,
996
997
  middleware: false
997
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
+ }
998
1007
  }
999
1008
  }
1000
1009
  sitemaps.index = {
@@ -1008,7 +1017,7 @@ declare module 'vue-router' {
1008
1017
  if (sitemapName === "index")
1009
1018
  continue;
1010
1019
  const definition = config.sitemaps[sitemapName];
1011
- sitemaps[sitemapName] = defu(
1020
+ const sitemapConfig = defu(
1012
1021
  {
1013
1022
  sitemapName,
1014
1023
  _route: withBase(joinURL(config.sitemapsPathPrefix || "", `${sitemapName}.xml`), nuxt.options.app.baseURL || "/"),
@@ -1017,6 +1026,28 @@ declare module 'vue-router' {
1017
1026
  { ...definition, urls: void 0, sources: void 0 },
1018
1027
  { include: config.include, exclude: config.exclude }
1019
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;
1020
1051
  }
1021
1052
  } else {
1022
1053
  sitemaps.chunks = {
@@ -1118,6 +1149,14 @@ declare module 'vue-router' {
1118
1149
  route: "/__sitemap__/debug.json",
1119
1150
  handler: resolve("./runtime/server/routes/__sitemap__/debug")
1120
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
+ }
1121
1160
  setupDevToolsUI(config, resolve);
1122
1161
  }
1123
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
  });
@@ -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
  });
@@ -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';
@@ -2,9 +2,9 @@ import { resolveSitePath } from "nuxt-site-config/urls";
2
2
  import { joinURL, withHttps } from "ufo";
3
3
  import { preNormalizeEntry } from "../urlset/normalise.js";
4
4
  import { childSitemapSources, globalSitemapSources, resolveSitemapSources } from "../urlset/sources.js";
5
- import { sortSitemapUrls } from "../urlset/sort.js";
5
+ import { sortInPlace } from "../urlset/sort.js";
6
6
  import { createPathFilter, logger, splitForLocales } from "../../../utils-pure.js";
7
- import { handleEntry, wrapSitemapXml } from "./xml.js";
7
+ import { parseChunkInfo, sliceUrlsForChunk } from "../utils/chunk.js";
8
8
  export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers) {
9
9
  const {
10
10
  autoI18n,
@@ -85,9 +85,23 @@ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers) {
85
85
  });
86
86
  } else {
87
87
  for (const l of autoI18n.locales) {
88
- let loc = joinURL(`/${l.code}`, e._pathWithoutPrefix);
89
- if (autoI18n.differentDomains || ["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy) && l.code === autoI18n.defaultLocale)
90
- loc = e._pathWithoutPrefix;
88
+ let loc = e._pathWithoutPrefix;
89
+ if (autoI18n.pages) {
90
+ const pageKey = e._pathWithoutPrefix.replace(/^\//, "").replace(/\/index$/, "") || "index";
91
+ const pageMappings = autoI18n.pages[pageKey];
92
+ if (pageMappings && pageMappings[l.code] !== void 0) {
93
+ const customPath = pageMappings[l.code];
94
+ if (customPath === false)
95
+ continue;
96
+ if (typeof customPath === "string")
97
+ loc = customPath.startsWith("/") ? customPath : `/${customPath}`;
98
+ } else if (!autoI18n.differentDomains && !(["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy) && l.code === autoI18n.defaultLocale)) {
99
+ loc = joinURL(`/${l.code}`, e._pathWithoutPrefix);
100
+ }
101
+ } else {
102
+ if (!autoI18n.differentDomains && !(["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy) && l.code === autoI18n.defaultLocale))
103
+ loc = joinURL(`/${l.code}`, e._pathWithoutPrefix);
104
+ }
91
105
  const _sitemap = isI18nMapped ? l._sitemap : void 0;
92
106
  const newEntry = preNormalizeEntry({
93
107
  _sitemap,
@@ -99,14 +113,30 @@ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers) {
99
113
  alternatives: [{ code: "x-default", _hreflang: "x-default" }, ...autoI18n.locales].map((locale) => {
100
114
  const code = locale.code === "x-default" ? autoI18n.defaultLocale : locale.code;
101
115
  const isDefault = locale.code === "x-default" || locale.code === autoI18n.defaultLocale;
102
- let href = "";
103
- if (autoI18n.strategy === "prefix") {
104
- href = joinURL("/", code, e._pathWithoutPrefix);
105
- } else if (["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy)) {
106
- if (isDefault) {
107
- href = e._pathWithoutPrefix;
108
- } else {
116
+ let href = e._pathWithoutPrefix;
117
+ if (autoI18n.pages) {
118
+ const pageKey = e._pathWithoutPrefix.replace(/^\//, "").replace(/\/index$/, "") || "index";
119
+ const pageMappings = autoI18n.pages[pageKey];
120
+ if (pageMappings && pageMappings[code] !== void 0) {
121
+ const customPath = pageMappings[code];
122
+ if (customPath === false)
123
+ return false;
124
+ if (typeof customPath === "string")
125
+ href = customPath.startsWith("/") ? customPath : `/${customPath}`;
126
+ } else if (autoI18n.strategy === "prefix") {
109
127
  href = joinURL("/", code, e._pathWithoutPrefix);
128
+ } else if (["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy)) {
129
+ if (!isDefault) {
130
+ href = joinURL("/", code, e._pathWithoutPrefix);
131
+ }
132
+ }
133
+ } else {
134
+ if (autoI18n.strategy === "prefix") {
135
+ href = joinURL("/", code, e._pathWithoutPrefix);
136
+ } else if (["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy)) {
137
+ if (!isDefault) {
138
+ href = joinURL("/", code, e._pathWithoutPrefix);
139
+ }
110
140
  }
111
141
  }
112
142
  if (!filterPath(href))
@@ -151,16 +181,12 @@ export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig, nitro)
151
181
  // chunking
152
182
  defaultSitemapsChunkSize
153
183
  } = runtimeConfig;
154
- const isChunking = typeof sitemaps.chunks !== "undefined" && !Number.isNaN(Number(sitemap.sitemapName));
184
+ const chunkInfo = parseChunkInfo(sitemap.sitemapName, sitemaps, defaultSitemapsChunkSize);
155
185
  function maybeSort(urls) {
156
- return sortEntries ? sortSitemapUrls(urls) : urls;
186
+ return sortEntries ? sortInPlace(urls) : urls;
157
187
  }
158
188
  function maybeSlice(urls) {
159
- if (isChunking && defaultSitemapsChunkSize) {
160
- const chunk = Number(sitemap.sitemapName);
161
- return urls.slice(chunk * defaultSitemapsChunkSize, (chunk + 1) * defaultSitemapsChunkSize);
162
- }
163
- return urls;
189
+ return sliceUrlsForChunk(urls, sitemap.sitemapName, sitemaps, defaultSitemapsChunkSize);
164
190
  }
165
191
  if (autoI18n?.differentDomains) {
166
192
  const domain = autoI18n.locales.find((e) => [e.language, e.code].includes(sitemap.sitemapName))?.domain;
@@ -175,8 +201,22 @@ export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig, nitro)
175
201
  });
176
202
  }
177
203
  }
178
- const sourcesInput = sitemap.includeAppSources ? await globalSitemapSources() : [];
179
- sourcesInput.push(...await childSitemapSources(sitemap));
204
+ let effectiveSitemap = sitemap;
205
+ const baseSitemapName = chunkInfo.baseSitemapName;
206
+ if (chunkInfo.isChunked && baseSitemapName !== sitemap.sitemapName && sitemaps[baseSitemapName]) {
207
+ effectiveSitemap = sitemaps[baseSitemapName];
208
+ }
209
+ let sourcesInput = effectiveSitemap.includeAppSources ? await globalSitemapSources() : [];
210
+ sourcesInput.push(...await childSitemapSources(effectiveSitemap));
211
+ if (nitro && resolvers.event) {
212
+ const ctx = {
213
+ event: resolvers.event,
214
+ sitemapName: baseSitemapName,
215
+ sources: sourcesInput
216
+ };
217
+ await nitro.hooks.callHook("sitemap:sources", ctx);
218
+ sourcesInput = ctx.sources;
219
+ }
180
220
  const sources = await resolveSitemapSources(sourcesInput, resolvers.event);
181
221
  const resolvedCtx = {
182
222
  urls: sources.flatMap((s) => s.urls),
@@ -193,18 +233,4 @@ export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig, nitro)
193
233
  const sortedUrls = maybeSort(filteredUrls);
194
234
  return maybeSlice(sortedUrls);
195
235
  }
196
- export function urlsToXml(urls, resolvers, { version, xsl, credits, minify }) {
197
- const urlset = urls.map((e) => {
198
- const keys = Object.keys(e).filter((k) => !k.startsWith("_"));
199
- return [
200
- " <url>",
201
- keys.map((k) => handleEntry(k, e)).filter(Boolean).join("\n"),
202
- " </url>"
203
- ].join("\n");
204
- });
205
- return wrapSitemapXml([
206
- '<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
207
- urlset.join("\n"),
208
- "</urlset>"
209
- ], resolvers, { version, xsl, credits, minify });
210
- }
236
+ export { urlsToXml } from "./xml.js";
@@ -1,4 +1,3 @@
1
- import type { ModuleRuntimeConfig, NitroUrlResolvers } from '../../../types.js';
2
- export declare function handleEntry(k: string, e: Record<string, any> | (string | Record<string, any>)[]): string | false;
3
- export declare function wrapSitemapXml(input: string[], resolvers: NitroUrlResolvers, options: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>): string;
1
+ import type { ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl } from '../../../types.js';
4
2
  export declare function escapeValueForXml(value: boolean | string | number): string;
3
+ export declare function urlsToXml(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>): string;