@nuxtjs/sitemap 7.4.11 → 7.5.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 (28) hide show
  1. package/dist/client/200.html +1 -1
  2. package/dist/client/404.html +1 -1
  3. package/dist/client/_nuxt/builds/latest.json +1 -1
  4. package/dist/client/_nuxt/builds/meta/cbb70b0f-de73-410d-92b2-7798fa3dd9b4.json +1 -0
  5. package/dist/client/index.html +1 -1
  6. package/dist/client/sitemap.xml +1 -1
  7. package/dist/module.d.mts +14 -2
  8. package/dist/module.d.ts +14 -2
  9. package/dist/module.json +1 -1
  10. package/dist/module.mjs +83 -31
  11. package/dist/runtime/server/routes/__zero-runtime/sitemap/[sitemap].xml.d.ts +2 -0
  12. package/dist/runtime/server/routes/__zero-runtime/sitemap/[sitemap].xml.js +8 -0
  13. package/dist/runtime/server/routes/__zero-runtime/sitemap.xml.d.ts +2 -0
  14. package/dist/runtime/server/routes/__zero-runtime/sitemap.xml.js +8 -0
  15. package/dist/runtime/server/routes/__zero-runtime/sitemap_index.xml.d.ts +2 -0
  16. package/dist/runtime/server/routes/__zero-runtime/sitemap_index.xml.js +8 -0
  17. package/dist/runtime/server/routes/sitemap/[sitemap].xml.d.ts +1 -1
  18. package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +3 -50
  19. package/dist/runtime/server/routes/sitemap.xml.js +3 -13
  20. package/dist/runtime/server/routes/sitemap_index.xml.js +3 -42
  21. package/dist/runtime/server/sitemap/builder/xml.js +1 -1
  22. package/dist/runtime/server/sitemap/event-handlers.d.ts +4 -0
  23. package/dist/runtime/server/sitemap/event-handlers.js +77 -0
  24. package/dist/runtime/server/sitemap/urlset/normalise.js +4 -2
  25. package/dist/runtime/types.d.ts +24 -0
  26. package/dist/types.d.mts +7 -1
  27. package/package.json +2 -2
  28. package/dist/client/_nuxt/builds/meta/ef370d03-581a-4487-a7bd-237af16aab04.json +0 -1
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__sitemap__/devtools/_nuxt/entry.Ci1pP-eR.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__sitemap__/devtools/_nuxt/FE81ed4p.js"><script type="module" src="/__sitemap__/devtools/_nuxt/FE81ed4p.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__={_priority:{name:-10,env:-15},env:"production",name:"@nuxtjs\u002Fsitemap-client"}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1765864560063,false]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__sitemap__/devtools",buildId:"ef370d03-581a-4487-a7bd-237af16aab04",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__sitemap__/devtools/_nuxt/entry.Ci1pP-eR.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__sitemap__/devtools/_nuxt/FE81ed4p.js"><script type="module" src="/__sitemap__/devtools/_nuxt/FE81ed4p.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__={_priority:{name:-10,env:-15},env:"production",name:"@nuxtjs\u002Fsitemap-client"}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1765942444324,false]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__sitemap__/devtools",buildId:"cbb70b0f-de73-410d-92b2-7798fa3dd9b4",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__sitemap__/devtools/_nuxt/entry.Ci1pP-eR.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__sitemap__/devtools/_nuxt/FE81ed4p.js"><script type="module" src="/__sitemap__/devtools/_nuxt/FE81ed4p.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__={_priority:{name:-10,env:-15},env:"production",name:"@nuxtjs\u002Fsitemap-client"}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1765864560063,false]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__sitemap__/devtools",buildId:"ef370d03-581a-4487-a7bd-237af16aab04",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__sitemap__/devtools/_nuxt/entry.Ci1pP-eR.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__sitemap__/devtools/_nuxt/FE81ed4p.js"><script type="module" src="/__sitemap__/devtools/_nuxt/FE81ed4p.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__={_priority:{name:-10,env:-15},env:"production",name:"@nuxtjs\u002Fsitemap-client"}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1765942444325,false]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__sitemap__/devtools",buildId:"cbb70b0f-de73-410d-92b2-7798fa3dd9b4",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
@@ -1 +1 @@
1
- {"id":"ef370d03-581a-4487-a7bd-237af16aab04","timestamp":1765864553677}
1
+ {"id":"cbb70b0f-de73-410d-92b2-7798fa3dd9b4","timestamp":1765942438634}
@@ -0,0 +1 @@
1
+ {"id":"cbb70b0f-de73-410d-92b2-7798fa3dd9b4","timestamp":1765942438634,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__sitemap__/devtools/_nuxt/entry.Ci1pP-eR.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__sitemap__/devtools/_nuxt/FE81ed4p.js"><script type="module" src="/__sitemap__/devtools/_nuxt/FE81ed4p.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__={_priority:{name:-10,env:-15},env:"production",name:"@nuxtjs\u002Fsitemap-client"}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1765864560063,false]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__sitemap__/devtools",buildId:"ef370d03-581a-4487-a7bd-237af16aab04",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__sitemap__/devtools/_nuxt/entry.Ci1pP-eR.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__sitemap__/devtools/_nuxt/FE81ed4p.js"><script type="module" src="/__sitemap__/devtools/_nuxt/FE81ed4p.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__={_priority:{name:-10,env:-15},env:"production",name:"@nuxtjs\u002Fsitemap-client"}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1765942444325,false]</script><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__sitemap__/devtools",buildId:"cbb70b0f-de73-410d-92b2-7798fa3dd9b4",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
@@ -4,4 +4,4 @@
4
4
  <loc>/__sitemap__/devtools</loc>
5
5
  </url>
6
6
  </urlset>
7
- <!-- XML Sitemap generated by @nuxtjs/sitemap v7.4.11 at 2025-12-16T05:56:00.589Z -->
7
+ <!-- XML Sitemap generated by @nuxtjs/sitemap v7.5.0 at 2025-12-17T03:34:04.847Z -->
package/dist/module.d.mts CHANGED
@@ -1,10 +1,22 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { ModuleOptions as ModuleOptions$1 } from '../dist/runtime/types.js';
2
+ import { ModuleOptions as ModuleOptions$1, ModuleRuntimeConfig } from '../dist/runtime/types.js';
3
3
  export * from '../dist/runtime/types.js';
4
4
 
5
5
  interface ModuleOptions extends ModuleOptions$1 {
6
6
  }
7
+ interface ModuleHooks {
8
+ /**
9
+ * Hook called after the prerender of the sitemaps is done.
10
+ */
11
+ 'sitemap:prerender:done': (ctx: {
12
+ options: ModuleRuntimeConfig;
13
+ sitemaps: {
14
+ name: string;
15
+ readonly content: string;
16
+ }[];
17
+ }) => void | Promise<void>;
18
+ }
7
19
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
8
20
 
9
21
  export { _default as default };
10
- export type { ModuleOptions };
22
+ export type { ModuleHooks, ModuleOptions };
package/dist/module.d.ts CHANGED
@@ -1,10 +1,22 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { ModuleOptions as ModuleOptions$1 } from '../dist/runtime/types.js';
2
+ import { ModuleOptions as ModuleOptions$1, ModuleRuntimeConfig } from '../dist/runtime/types.js';
3
3
  export * from '../dist/runtime/types.js';
4
4
 
5
5
  interface ModuleOptions extends ModuleOptions$1 {
6
6
  }
7
+ interface ModuleHooks {
8
+ /**
9
+ * Hook called after the prerender of the sitemaps is done.
10
+ */
11
+ 'sitemap:prerender:done': (ctx: {
12
+ options: ModuleRuntimeConfig;
13
+ sitemaps: {
14
+ name: string;
15
+ readonly content: string;
16
+ }[];
17
+ }) => void | Promise<void>;
18
+ }
7
19
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
8
20
 
9
21
  export { _default as default };
10
- export type { ModuleOptions };
22
+ export type { ModuleHooks, ModuleOptions };
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=3.9.0"
5
5
  },
6
6
  "configKey": "sitemap",
7
- "version": "7.4.11",
7
+ "version": "7.5.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -4,7 +4,7 @@ import { withSiteUrl, installNuxtSiteConfig } from 'nuxt-site-config/kit';
4
4
  import { defu } from 'defu';
5
5
  import { readPackageJSON } from 'pkg-types';
6
6
  import { extname, dirname, relative } from 'pathe';
7
- import { statSync, existsSync } from 'node:fs';
7
+ import { statSync, readFileSync, existsSync } from 'node:fs';
8
8
  import { createPathFilter, splitForLocales, mergeOnKey } from '../dist/runtime/utils-pure.js';
9
9
  import { provider, env } from 'std-env';
10
10
  import { mkdir, writeFile } from 'node:fs/promises';
@@ -320,7 +320,9 @@ export async function readSourcesFromFilesystem(filename) {
320
320
  videos: options.discoverVideos,
321
321
  // TODO configurable?
322
322
  lastmod: true,
323
- alternatives: true,
323
+ // when autoI18n is enabled, let the sitemap builder generate alternatives
324
+ // based on i18n config instead of extracting from HTML (which can be incomplete)
325
+ alternatives: !options.autoI18n,
324
326
  resolveUrl(s) {
325
327
  return s.startsWith("/") ? withSiteUrl(s) : s;
326
328
  }
@@ -352,10 +354,31 @@ export async function readSourcesFromFilesystem(filename) {
352
354
  await mkdir(runtimeAssetsPath, { recursive: true });
353
355
  await writeFile(join(runtimeAssetsPath, "global-sources.json"), JSON.stringify(globalSources));
354
356
  await writeFile(join(runtimeAssetsPath, "child-sources.json"), JSON.stringify(childSources));
355
- await prerenderRoute(nitro, options.isMultiSitemap ? "/sitemap_index.xml" : `/${Object.keys(options.sitemaps)[0]}`);
357
+ const sitemapEntry = options.isMultiSitemap ? "/sitemap_index.xml" : `/${Object.keys(options.sitemaps)[0]}`;
358
+ const sitemaps = await prerenderSitemapsFromEntry(nitro, sitemapEntry);
359
+ await nuxt.hooks.callHook("sitemap:prerender:done", { options, sitemaps });
356
360
  });
357
361
  });
358
362
  }
363
+ async function prerenderSitemapsFromEntry(nitro, entry) {
364
+ const sitemaps = [];
365
+ const queue = [entry];
366
+ const processed = /* @__PURE__ */ new Set();
367
+ while (queue.length) {
368
+ const route = queue.shift();
369
+ if (processed.has(route)) continue;
370
+ processed.add(route);
371
+ const { filePath, prerenderUrls } = await prerenderRoute(nitro, route);
372
+ sitemaps.push({
373
+ name: route,
374
+ get content() {
375
+ return readFileSync(filePath, { encoding: "utf8" });
376
+ }
377
+ });
378
+ queue.push(...prerenderUrls);
379
+ }
380
+ return sitemaps;
381
+ }
359
382
  async function prerenderRoute(nitro, route) {
360
383
  const start = Date.now();
361
384
  const _route = { route, fileName: route };
@@ -370,23 +393,18 @@ async function prerenderRoute(nitro, route) {
370
393
  }
371
394
  );
372
395
  const header = res.headers.get("x-nitro-prerender") || "";
373
- const prerenderUrls = [
374
- ...header.split(",").map((i) => i.trim()).map((i) => decodeURIComponent(i)).filter(Boolean)
375
- ];
396
+ const prerenderUrls = header.split(",").map((i) => decodeURIComponent(i.trim())).filter(Boolean);
376
397
  const filePath = join(nitro.options.output.publicDir, _route.fileName);
377
398
  await mkdir(dirname(filePath), { recursive: true });
378
399
  const data = res._data;
379
400
  if (data === void 0)
380
401
  throw new Error(`No data returned from '${fetchUrl}'`);
381
- if (filePath.endsWith("json") || typeof data === "object")
382
- await writeFile(filePath, JSON.stringify(data), "utf8");
383
- else
384
- await writeFile(filePath, data, "utf8");
402
+ const content = filePath.endsWith("json") || typeof data === "object" ? JSON.stringify(data) : data;
403
+ await writeFile(filePath, content, "utf8");
385
404
  _route.generateTimeMS = Date.now() - start;
386
405
  nitro._prerenderedRoutes.push(_route);
387
406
  nitro.logger.log(formatPrerenderRoute(_route));
388
- for (const url of prerenderUrls)
389
- await prerenderRoute(nitro, url);
407
+ return { filePath, prerenderUrls };
390
408
  }
391
409
 
392
410
  const DEVTOOLS_UI_ROUTE = "/__sitemap__/devtools";
@@ -557,7 +575,8 @@ const module$1 = defineNuxtModule({
557
575
  exclude: ["/_**"],
558
576
  // sources
559
577
  sources: [],
560
- excludeAppSources: []
578
+ excludeAppSources: [],
579
+ zeroRuntime: false
561
580
  },
562
581
  async setup(config, nuxt) {
563
582
  const { resolve } = createResolver(import.meta.url);
@@ -750,7 +769,7 @@ const module$1 = defineNuxtModule({
750
769
  'sitemap:output': (ctx: import('${typesPath}').SitemapOutputHookCtx) => void | Promise<void>
751
770
  'sitemap:sources': (ctx: import('${typesPath}').SitemapSourcesHookCtx) => void | Promise<void>
752
771
  }`;
753
- return `// Generated by nuxt-robots
772
+ return `// Generated by @nuxtjs/sitemap
754
773
  declare module 'nitropack' {
755
774
  ${types}
756
775
  }
@@ -767,11 +786,20 @@ export {}
767
786
  `;
768
787
  }
769
788
  }, {
789
+ node: true,
770
790
  nitro: true,
771
791
  nuxt: true
772
792
  });
773
793
  const prerenderedRoutes = nuxt.options.nitro.prerender?.routes || [];
774
- const prerenderSitemap = isNuxtGenerate() || includesSitemapRoot(config.sitemapName, prerenderedRoutes);
794
+ let prerenderSitemap = isNuxtGenerate() || includesSitemapRoot(config.sitemapName, prerenderedRoutes);
795
+ if (config.zeroRuntime && !prerenderSitemap) {
796
+ prerenderSitemap = true;
797
+ nuxt.options.nitro.prerender = nuxt.options.nitro.prerender || {};
798
+ nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes || [];
799
+ nuxt.options.nitro.prerender.routes.push("/sitemap.xml");
800
+ logger.info("`zeroRuntime` enabled - sitemap routes will be prerendered.");
801
+ }
802
+ const routesPath = config.zeroRuntime ? "./runtime/server/routes/__zero-runtime" : "./runtime/server/routes";
775
803
  const routeRules = {};
776
804
  nuxt.options.nitro.routeRules = nuxt.options.nitro.routeRules || {};
777
805
  if (prerenderSitemap) {
@@ -807,10 +835,14 @@ export {}
807
835
  } else {
808
836
  nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
809
837
  }
810
- if (config.experimentalWarmUp)
811
- addServerPlugin(resolve("./runtime/server/plugins/warm-up"));
812
- if (config.experimentalCompression)
813
- addServerPlugin(resolve("./runtime/server/plugins/compression"));
838
+ if (config.zeroRuntime && (config.experimentalWarmUp || config.experimentalCompression))
839
+ logger.warn("`experimentalWarmUp` and `experimentalCompression` are ignored in zeroRuntime mode.");
840
+ if (!config.zeroRuntime) {
841
+ if (config.experimentalWarmUp)
842
+ addServerPlugin(resolve("./runtime/server/plugins/warm-up"));
843
+ if (config.experimentalCompression)
844
+ addServerPlugin(resolve("./runtime/server/plugins/compression"));
845
+ }
814
846
  const isNuxtContentDocumentDriven = !!nuxt.options.content?.documentDriven || config.strictNuxtContentPaths;
815
847
  const usingNuxtContent = hasNuxtModule("@nuxt/content");
816
848
  const isNuxtContentV3 = usingNuxtContent && await hasNuxtModuleCompatibility("@nuxt/content", "^3");
@@ -904,36 +936,49 @@ export {}
904
936
  if (usingMultiSitemaps) {
905
937
  addServerHandler({
906
938
  route: "/sitemap_index.xml",
907
- handler: resolve("./runtime/server/routes/sitemap_index.xml"),
939
+ handler: resolve(`${routesPath}/sitemap_index.xml`),
908
940
  lazy: true,
909
941
  middleware: false
910
942
  });
911
943
  if (config.sitemapsPathPrefix && config.sitemapsPathPrefix !== "/") {
912
944
  addServerHandler({
913
945
  route: joinURL(config.sitemapsPathPrefix, `/**:sitemap`),
914
- handler: resolve("./runtime/server/routes/sitemap/[sitemap].xml"),
946
+ handler: resolve(`${routesPath}/sitemap/[sitemap].xml`),
915
947
  lazy: true,
916
948
  middleware: false
917
949
  });
918
950
  } else {
919
951
  const sitemapNames = Object.keys(config.sitemaps || {});
952
+ let hasChunkedSitemaps = false;
920
953
  for (const sitemapName of sitemapNames) {
921
954
  if (sitemapName === "index")
922
955
  continue;
923
956
  const sitemapConfig = config.sitemaps[sitemapName];
924
957
  addServerHandler({
925
958
  route: withLeadingSlash(`${sitemapName}.xml`),
926
- handler: resolve("./runtime/server/routes/sitemap/[sitemap].xml"),
959
+ handler: resolve(`${routesPath}/sitemap/[sitemap].xml`),
927
960
  lazy: true,
928
961
  middleware: false
929
962
  });
930
- if (sitemapConfig.chunks) {
931
- addServerHandler({
932
- route: `/${sitemapName}-*.xml`,
933
- handler: resolve("./runtime/server/routes/sitemap/[sitemap].xml"),
934
- lazy: true,
935
- middleware: false
936
- });
963
+ if (sitemapConfig.chunks)
964
+ hasChunkedSitemaps = true;
965
+ }
966
+ if (hasChunkedSitemaps) {
967
+ const maxChunks = 20;
968
+ for (const sitemapName of sitemapNames) {
969
+ if (sitemapName === "index")
970
+ continue;
971
+ const sitemapConfig = config.sitemaps[sitemapName];
972
+ if (sitemapConfig.chunks) {
973
+ for (let i = 0; i < maxChunks; i++) {
974
+ addServerHandler({
975
+ route: `/${sitemapName}-${i}.xml`,
976
+ handler: resolve(`${routesPath}/sitemap/[sitemap].xml`),
977
+ lazy: true,
978
+ middleware: false
979
+ });
980
+ }
981
+ }
937
982
  }
938
983
  }
939
984
  }
@@ -1075,7 +1120,7 @@ export {}
1075
1120
  if (resolvedAutoI18n)
1076
1121
  runtimeConfig.autoI18n = resolvedAutoI18n;
1077
1122
  nuxt.options.runtimeConfig.sitemap = runtimeConfig;
1078
- if (config.debug || nuxt.options.dev) {
1123
+ if ((config.debug || nuxt.options.dev) && !config.zeroRuntime) {
1079
1124
  addServerHandler({
1080
1125
  route: "/__sitemap__/debug.json",
1081
1126
  handler: resolve("./runtime/server/routes/__sitemap__/debug")
@@ -1271,9 +1316,16 @@ export async function readSourcesFromFilesystem() {
1271
1316
  }
1272
1317
  addServerHandler({
1273
1318
  route: `/${config.sitemapName}`,
1274
- handler: resolve("./runtime/server/routes/sitemap.xml")
1319
+ handler: resolve(`${routesPath}/sitemap.xml`)
1275
1320
  });
1276
1321
  setupPrerenderHandler({ runtimeConfig, logger, generateGlobalSources, generateChildSources });
1322
+ if (!config.zeroRuntime && !nuxt.options.dev && !nuxt.options._prepare) {
1323
+ const hasDynamicSource = (source) => typeof source === "string" || Array.isArray(source) || !!source.fetch;
1324
+ const globalHasFetch = (config.sources || []).some(hasDynamicSource);
1325
+ const sitemapsHaveFetch = typeof config.sitemaps === "object" && Object.values(config.sitemaps).some((s) => s && "sources" in s && (s.sources || []).some(hasDynamicSource));
1326
+ if (!globalHasFetch && !sitemapsHaveFetch)
1327
+ logger.info("No dynamic sources detected. Consider enabling `zeroRuntime` to reduce server bundle size. See https://nuxtseo.com/sitemap/guides/zero-runtime");
1328
+ }
1277
1329
  }
1278
1330
  });
1279
1331
 
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | undefined>>;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import { createError, defineEventHandler } from "h3";
2
+ export default defineEventHandler(async (e) => {
3
+ if (import.meta.dev || import.meta.prerender) {
4
+ const { sitemapChildXmlEventHandler } = await import("../../../sitemap/event-handlers.js");
5
+ return sitemapChildXmlEventHandler(e);
6
+ }
7
+ throw createError({ statusCode: 500, message: "Sitemap not prerendered. zeroRuntime requires prerendering." });
8
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | void>>;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import { createError, defineEventHandler } from "h3";
2
+ export default defineEventHandler(async (e) => {
3
+ if (import.meta.dev || import.meta.prerender) {
4
+ const { sitemapXmlEventHandler } = await import("../../sitemap/event-handlers.js");
5
+ return sitemapXmlEventHandler(e);
6
+ }
7
+ throw createError({ statusCode: 500, message: "Sitemap not prerendered. zeroRuntime requires prerendering." });
8
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string>>;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import { createError, defineEventHandler } from "h3";
2
+ export default defineEventHandler(async (e) => {
3
+ if (import.meta.dev || import.meta.prerender) {
4
+ const { sitemapIndexXmlEventHandler } = await import("../../sitemap/event-handlers.js");
5
+ return sitemapIndexXmlEventHandler(e);
6
+ }
7
+ throw createError({ statusCode: 500, message: "Sitemap not prerendered. zeroRuntime requires prerendering." });
8
+ });
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | import("h3").H3Error<unknown>>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | undefined>>;
2
2
  export default _default;
@@ -1,50 +1,3 @@
1
- import { createError, defineEventHandler, getRouterParam } from "h3";
2
- import { withoutLeadingSlash, withoutTrailingSlash } from "ufo";
3
- import { useSitemapRuntimeConfig } from "../../utils.js";
4
- import { createSitemap } from "../../sitemap/nitro.js";
5
- import { parseChunkInfo, getSitemapConfig } from "../../sitemap/utils/chunk.js";
6
- export default defineEventHandler(async (e) => {
7
- const runtimeConfig = useSitemapRuntimeConfig(e);
8
- const { sitemaps } = runtimeConfig;
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) {
28
- return createError({
29
- statusCode: 404,
30
- message: `Sitemap "${sitemapName}" not found.`
31
- });
32
- }
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 || void 0);
49
- return createSitemap(e, sitemapConfig, runtimeConfig);
50
- });
1
+ import { defineEventHandler } from "h3";
2
+ import { sitemapChildXmlEventHandler } from "../../sitemap/event-handlers.js";
3
+ export default defineEventHandler(sitemapChildXmlEventHandler);
@@ -1,13 +1,3 @@
1
- import { defineEventHandler, sendRedirect } from "h3";
2
- import { withBase } from "ufo";
3
- import { useRuntimeConfig } from "nitropack/runtime";
4
- import { useSitemapRuntimeConfig } from "../utils.js";
5
- import { createSitemap } from "../sitemap/nitro.js";
6
- export default defineEventHandler(async (e) => {
7
- const runtimeConfig = useSitemapRuntimeConfig();
8
- const { sitemaps } = runtimeConfig;
9
- if ("index" in sitemaps) {
10
- return sendRedirect(e, withBase("/sitemap_index.xml", useRuntimeConfig().app.baseURL), import.meta.dev ? 302 : 301);
11
- }
12
- return createSitemap(e, Object.values(sitemaps)[0], runtimeConfig);
13
- });
1
+ import { defineEventHandler } from "h3";
2
+ import { sitemapXmlEventHandler } from "../sitemap/event-handlers.js";
3
+ export default defineEventHandler(sitemapXmlEventHandler);
@@ -1,42 +1,3 @@
1
- import { appendHeader, defineEventHandler, setHeader } from "h3";
2
- import { joinURL } from "ufo";
3
- import { useNitroApp } from "nitropack/runtime";
4
- import { useSitemapRuntimeConfig } from "../utils.js";
5
- import { buildSitemapIndex, urlsToIndexXml } from "../sitemap/builder/sitemap-index.js";
6
- import { useNitroUrlResolvers } from "../sitemap/nitro.js";
7
- export default defineEventHandler(async (e) => {
8
- const runtimeConfig = useSitemapRuntimeConfig();
9
- const nitro = useNitroApp();
10
- const resolvers = useNitroUrlResolvers(e);
11
- const { entries: sitemaps, failedSources } = await buildSitemapIndex(resolvers, runtimeConfig, nitro);
12
- if (import.meta.prerender) {
13
- appendHeader(
14
- e,
15
- "x-nitro-prerender",
16
- sitemaps.filter((entry) => !!entry._sitemapName).map((entry) => encodeURIComponent(joinURL(runtimeConfig.sitemapsPathPrefix || "", `/${entry._sitemapName}.xml`))).join(", ")
17
- );
18
- }
19
- const indexResolvedCtx = { sitemaps, event: e };
20
- await nitro.hooks.callHook("sitemap:index-resolved", indexResolvedCtx);
21
- const errorInfo = failedSources.length > 0 ? {
22
- messages: failedSources.map((f) => f.error),
23
- urls: failedSources.map((f) => f.url)
24
- } : void 0;
25
- const output = urlsToIndexXml(indexResolvedCtx.sitemaps, resolvers, runtimeConfig, errorInfo);
26
- const ctx = { sitemap: output, sitemapName: "sitemap", event: e };
27
- await nitro.hooks.callHook("sitemap:output", ctx);
28
- setHeader(e, "Content-Type", "text/xml; charset=UTF-8");
29
- if (runtimeConfig.cacheMaxAgeSeconds) {
30
- setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
31
- const now = /* @__PURE__ */ new Date();
32
- setHeader(e, "X-Sitemap-Generated", now.toISOString());
33
- setHeader(e, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
34
- const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
35
- setHeader(e, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
36
- const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
37
- setHeader(e, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
38
- } else {
39
- setHeader(e, "Cache-Control", `no-cache, no-store`);
40
- }
41
- return ctx.sitemap;
42
- });
1
+ import { defineEventHandler } from "h3";
2
+ import { sitemapIndexXmlEventHandler } from "../sitemap/event-handlers.js";
3
+ export default defineEventHandler(sitemapIndexXmlEventHandler);
@@ -14,7 +14,7 @@ function buildUrlXml(url, NL, I1, I2, I3, I4) {
14
14
  if (url.changefreq) xml += `${I2}<changefreq>${url.changefreq}</changefreq>${NL}`;
15
15
  if (url.priority !== void 0) {
16
16
  const p = typeof url.priority === "number" ? url.priority : Number.parseFloat(url.priority);
17
- xml += `${I2}<priority>${p % 1 === 0 ? p : p.toFixed(1)}</priority>${NL}`;
17
+ xml += `${I2}<priority>${p.toFixed(1)}</priority>${NL}`;
18
18
  }
19
19
  if (url.alternatives) {
20
20
  for (const alt of url.alternatives) {
@@ -0,0 +1,4 @@
1
+ import type { H3Event } from 'h3';
2
+ export declare function sitemapXmlEventHandler(e: H3Event): Promise<string | void>;
3
+ export declare function sitemapIndexXmlEventHandler(e: H3Event): Promise<string>;
4
+ export declare function sitemapChildXmlEventHandler(e: H3Event): Promise<string | undefined>;
@@ -0,0 +1,77 @@
1
+ import { appendHeader, createError, getRouterParam, sendRedirect, setHeader } from "h3";
2
+ import { joinURL, withBase, withoutLeadingSlash, withoutTrailingSlash } from "ufo";
3
+ import { useRuntimeConfig, useNitroApp } from "nitropack/runtime";
4
+ import { useSitemapRuntimeConfig } from "../utils.js";
5
+ import { createSitemap, useNitroUrlResolvers } from "./nitro.js";
6
+ import { buildSitemapIndex, urlsToIndexXml } from "./builder/sitemap-index.js";
7
+ import { parseChunkInfo, getSitemapConfig } from "./utils/chunk.js";
8
+ export async function sitemapXmlEventHandler(e) {
9
+ const runtimeConfig = useSitemapRuntimeConfig();
10
+ const { sitemaps } = runtimeConfig;
11
+ if ("index" in sitemaps)
12
+ return sendRedirect(e, withBase("/sitemap_index.xml", useRuntimeConfig().app.baseURL), import.meta.dev ? 302 : 301);
13
+ return createSitemap(e, Object.values(sitemaps)[0], runtimeConfig);
14
+ }
15
+ export async function sitemapIndexXmlEventHandler(e) {
16
+ const runtimeConfig = useSitemapRuntimeConfig();
17
+ const nitro = useNitroApp();
18
+ const resolvers = useNitroUrlResolvers(e);
19
+ const { entries: sitemaps, failedSources } = await buildSitemapIndex(resolvers, runtimeConfig, nitro);
20
+ if (import.meta.prerender) {
21
+ appendHeader(
22
+ e,
23
+ "x-nitro-prerender",
24
+ sitemaps.filter((entry) => !!entry._sitemapName).map((entry) => encodeURIComponent(joinURL(runtimeConfig.sitemapsPathPrefix || "", `/${entry._sitemapName}.xml`))).join(", ")
25
+ );
26
+ }
27
+ const indexResolvedCtx = { sitemaps, event: e };
28
+ await nitro.hooks.callHook("sitemap:index-resolved", indexResolvedCtx);
29
+ const errorInfo = failedSources.length > 0 ? { messages: failedSources.map((f) => f.error), urls: failedSources.map((f) => f.url) } : void 0;
30
+ const output = urlsToIndexXml(indexResolvedCtx.sitemaps, resolvers, runtimeConfig, errorInfo);
31
+ const ctx = { sitemap: output, sitemapName: "sitemap", event: e };
32
+ await nitro.hooks.callHook("sitemap:output", ctx);
33
+ setHeader(e, "Content-Type", "text/xml; charset=UTF-8");
34
+ if (runtimeConfig.cacheMaxAgeSeconds) {
35
+ setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
36
+ const now = /* @__PURE__ */ new Date();
37
+ setHeader(e, "X-Sitemap-Generated", now.toISOString());
38
+ setHeader(e, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
39
+ const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
40
+ setHeader(e, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
41
+ const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
42
+ setHeader(e, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
43
+ } else {
44
+ setHeader(e, "Cache-Control", `no-cache, no-store`);
45
+ }
46
+ return ctx.sitemap;
47
+ }
48
+ export async function sitemapChildXmlEventHandler(e) {
49
+ if (!e.path.endsWith(".xml"))
50
+ return;
51
+ const runtimeConfig = useSitemapRuntimeConfig(e);
52
+ const { sitemaps } = runtimeConfig;
53
+ let sitemapName = getRouterParam(e, "sitemap");
54
+ if (!sitemapName) {
55
+ const path = e.path;
56
+ const match = path.match(/(?:\/__sitemap__\/)?([^/]+)\.xml$/);
57
+ if (match)
58
+ sitemapName = match[1];
59
+ }
60
+ if (!sitemapName)
61
+ throw createError({ statusCode: 400, message: "Invalid sitemap request" });
62
+ sitemapName = withoutLeadingSlash(withoutTrailingSlash(sitemapName.replace(".xml", "").replace("__sitemap__/", "").replace(runtimeConfig.sitemapsPathPrefix || "", "")));
63
+ const chunkInfo = parseChunkInfo(sitemapName, sitemaps, runtimeConfig.defaultSitemapsChunkSize);
64
+ const isAutoChunked = typeof sitemaps.chunks !== "undefined" && !Number.isNaN(Number(sitemapName));
65
+ const sitemapExists = sitemapName in sitemaps || chunkInfo.baseSitemapName in sitemaps || isAutoChunked;
66
+ if (!sitemapExists)
67
+ throw createError({ statusCode: 404, message: `Sitemap "${sitemapName}" not found.` });
68
+ if (chunkInfo.isChunked && chunkInfo.chunkIndex !== void 0) {
69
+ const baseSitemap = sitemaps[chunkInfo.baseSitemapName];
70
+ if (baseSitemap && !baseSitemap.chunks && !baseSitemap._isChunking)
71
+ throw createError({ statusCode: 404, message: `Sitemap "${chunkInfo.baseSitemapName}" does not support chunking.` });
72
+ if (baseSitemap?._chunkCount !== void 0 && chunkInfo.chunkIndex >= baseSitemap._chunkCount)
73
+ throw createError({ statusCode: 404, message: `Chunk ${chunkInfo.chunkIndex} does not exist for sitemap "${chunkInfo.baseSitemapName}".` });
74
+ }
75
+ const sitemapConfig = getSitemapConfig(sitemapName, sitemaps, runtimeConfig.defaultSitemapsChunkSize || void 0);
76
+ return createSitemap(e, sitemapConfig, runtimeConfig);
77
+ }
@@ -32,6 +32,7 @@ export function preNormalizeEntry(_e, resolvers) {
32
32
  if (typeof input.loc !== "string") {
33
33
  input.loc = "";
34
34
  }
35
+ const skipEncoding = input._encoded === true;
35
36
  const e = input;
36
37
  e.loc = removeTrailingSlash(e.loc);
37
38
  e._abs = hasProtocol(e.loc, { acceptRelative: false, strict: false });
@@ -43,13 +44,14 @@ export function preNormalizeEntry(_e, resolvers) {
43
44
  if (e._path) {
44
45
  const search = e._path.search;
45
46
  const qs = search && search.length > 1 ? stringifyQuery(parseQuery(search)) : "";
46
- e._relativeLoc = `${encodePath(e._path.pathname)}${qs.length ? `?${qs}` : ""}`;
47
+ const pathname = skipEncoding ? e._path.pathname : encodePath(e._path.pathname);
48
+ e._relativeLoc = `${pathname}${qs.length ? `?${qs}` : ""}`;
47
49
  if (e._path.host) {
48
50
  e.loc = stringifyParsedURL(e._path);
49
51
  } else {
50
52
  e.loc = e._relativeLoc;
51
53
  }
52
- } else if (!isEncoded(e.loc)) {
54
+ } else if (!skipEncoding && !isEncoded(e.loc)) {
53
55
  e.loc = encodeURI(e.loc);
54
56
  }
55
57
  if (e.loc === "")
@@ -153,6 +153,15 @@ export interface ModuleOptions extends SitemapDefinition {
153
153
  * @experimental Will be enabled by default in v5 (if stable)
154
154
  */
155
155
  experimentalCompression?: boolean;
156
+ /**
157
+ * When enabled, sitemap generation only runs during prerendering.
158
+ * The sitemap building code is tree-shaken from the runtime bundle.
159
+ *
160
+ * Requires sitemaps to be prerendered (e.g., `nuxt generate` or `nitro.prerender.routes` includes sitemap).
161
+ *
162
+ * @default false
163
+ */
164
+ zeroRuntime?: boolean;
156
165
  }
157
166
  export interface IndexSitemapRemotes {
158
167
  index?: (string | SitemapIndexEntry)[];
@@ -375,6 +384,21 @@ export interface SitemapUrl {
375
384
  videos?: Array<VideoEntry>;
376
385
  _i18nTransform?: boolean;
377
386
  _sitemap?: string | false;
387
+ /**
388
+ * Mark the URL as already encoded.
389
+ *
390
+ * When true, the loc will not be automatically encoded, preventing double-encoding
391
+ * when you've already applied encodeURIComponent() to path segments.
392
+ *
393
+ * @example
394
+ * ```ts
395
+ * {
396
+ * loc: `/${encodeURIComponent('$pecial-char')}`,
397
+ * _encoded: true
398
+ * }
399
+ * ```
400
+ */
401
+ _encoded?: boolean;
378
402
  }
379
403
  export type SitemapStrict = Required<SitemapUrl>;
380
404
  export interface AlternativeEntry {
package/dist/types.d.mts CHANGED
@@ -1,5 +1,11 @@
1
+ import type { ModuleHooks } from './module.mjs'
2
+
3
+ declare module '@nuxt/schema' {
4
+ interface NuxtHooks extends ModuleHooks {}
5
+ }
6
+
1
7
  export { default } from './module.mjs'
2
8
 
3
- export { type ModuleOptions } from './module.mjs'
9
+ export { type ModuleHooks, type ModuleOptions } from './module.mjs'
4
10
 
5
11
  export * from '../dist/runtime/types.js'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/sitemap",
3
3
  "type": "module",
4
- "version": "7.4.11",
4
+ "version": "7.5.0",
5
5
  "description": "Powerfully flexible XML Sitemaps that integrate seamlessly, for Nuxt.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -93,7 +93,7 @@
93
93
  "typescript": "^5.9.3",
94
94
  "vitest": "3.2.4",
95
95
  "vue-tsc": "^3.1.8",
96
- "@nuxtjs/sitemap": "7.4.11"
96
+ "@nuxtjs/sitemap": "7.5.0"
97
97
  },
98
98
  "scripts": {
99
99
  "lint": "eslint .",
@@ -1 +0,0 @@
1
- {"id":"ef370d03-581a-4487-a7bd-237af16aab04","timestamp":1765864553677,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}