@nuxtjs/sitemap 7.4.11 → 7.5.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 (41) hide show
  1. package/dist/client/200.html +1 -1
  2. package/dist/client/404.html +1 -1
  3. package/dist/client/_nuxt/BZzBMR0a.js +155 -0
  4. package/dist/client/_nuxt/{CjT5ejtq.js → BcLzIRTJ.js} +1 -1
  5. package/dist/client/_nuxt/{DBmpb9dG.js → CQIolwG-.js} +1 -1
  6. package/dist/client/_nuxt/builds/latest.json +1 -1
  7. package/dist/client/_nuxt/builds/meta/c9c08595-dc8d-4073-931c-6ba959692bc1.json +1 -0
  8. package/dist/client/_nuxt/error-404.DKXSOdpM.css +1 -0
  9. package/dist/client/_nuxt/error-500.rUXo3Kq4.css +1 -0
  10. package/dist/client/index.html +1 -1
  11. package/dist/client/sitemap.xml +1 -1
  12. package/dist/module.d.mts +14 -2
  13. package/dist/module.d.ts +14 -2
  14. package/dist/module.json +1 -1
  15. package/dist/module.mjs +83 -31
  16. package/dist/runtime/server/routes/__zero-runtime/sitemap/[sitemap].xml.d.ts +2 -0
  17. package/dist/runtime/server/routes/__zero-runtime/sitemap/[sitemap].xml.js +8 -0
  18. package/dist/runtime/server/routes/__zero-runtime/sitemap.xml.d.ts +2 -0
  19. package/dist/runtime/server/routes/__zero-runtime/sitemap.xml.js +8 -0
  20. package/dist/runtime/server/routes/__zero-runtime/sitemap_index.xml.d.ts +2 -0
  21. package/dist/runtime/server/routes/__zero-runtime/sitemap_index.xml.js +8 -0
  22. package/dist/runtime/server/routes/sitemap/[sitemap].xml.d.ts +1 -1
  23. package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +3 -50
  24. package/dist/runtime/server/routes/sitemap.xml.js +3 -13
  25. package/dist/runtime/server/routes/sitemap_index.xml.js +3 -42
  26. package/dist/runtime/server/sitemap/builder/sitemap.js +16 -13
  27. package/dist/runtime/server/sitemap/builder/xml.js +1 -1
  28. package/dist/runtime/server/sitemap/event-handlers.d.ts +4 -0
  29. package/dist/runtime/server/sitemap/event-handlers.js +77 -0
  30. package/dist/runtime/server/sitemap/urlset/normalise.js +4 -2
  31. package/dist/runtime/server/sitemap/urlset/sources.d.ts +3 -2
  32. package/dist/runtime/server/sitemap/urlset/sources.js +16 -6
  33. package/dist/runtime/types.d.ts +25 -1
  34. package/dist/runtime/utils-pure.d.ts +6 -0
  35. package/dist/runtime/utils-pure.js +20 -0
  36. package/dist/types.d.mts +7 -1
  37. package/package.json +8 -7
  38. package/dist/client/_nuxt/FE81ed4p.js +0 -155
  39. package/dist/client/_nuxt/builds/meta/ef370d03-581a-4487-a7bd-237af16aab04.json +0 -1
  40. package/dist/client/_nuxt/error-404.DC9fsYfS.css +0 -1
  41. package/dist/client/_nuxt/error-500.DPVweS-0.css +0 -1
@@ -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 === "")
@@ -1,6 +1,7 @@
1
1
  import type { H3Event } from 'h3';
2
- import type { ModuleRuntimeConfig, SitemapSourceBase, SitemapSourceResolved } from '../../../types.js';
2
+ import type { ModuleRuntimeConfig, SitemapSourceBase, SitemapSourceInput, SitemapSourceResolved } from '../../../types.js';
3
+ export declare function normalizeSourceInput(source: SitemapSourceInput): SitemapSourceBase | SitemapSourceResolved;
3
4
  export declare function fetchDataSource(input: SitemapSourceBase | SitemapSourceResolved, event?: H3Event): Promise<SitemapSourceResolved>;
4
5
  export declare function globalSitemapSources(): Promise<any[]>;
5
6
  export declare function childSitemapSources(definition: ModuleRuntimeConfig['sitemaps'][string]): Promise<any[]>;
6
- export declare function resolveSitemapSources(sources: (SitemapSourceBase | SitemapSourceResolved)[], event?: H3Event): Promise<SitemapSourceResolved[]>;
7
+ export declare function resolveSitemapSources(sources: SitemapSourceInput[], event?: H3Event): Promise<SitemapSourceResolved[]>;
@@ -3,6 +3,15 @@ import { defu } from "defu";
3
3
  import { parseURL } from "ufo";
4
4
  import { logger } from "../../../utils-pure.js";
5
5
  import { parseSitemapXml } from "@nuxtjs/sitemap/utils";
6
+ export function normalizeSourceInput(source) {
7
+ if (typeof source === "string") {
8
+ return { context: { name: "hook" }, fetch: source };
9
+ }
10
+ if (Array.isArray(source)) {
11
+ return { context: { name: "hook" }, fetch: source };
12
+ }
13
+ return source;
14
+ }
6
15
  async function tryFetchWithFallback(url, options, event) {
7
16
  const isExternalUrl = !url.startsWith("/");
8
17
  if (isExternalUrl) {
@@ -143,17 +152,18 @@ export async function childSitemapSources(definition) {
143
152
  export async function resolveSitemapSources(sources, event) {
144
153
  return (await Promise.all(
145
154
  sources.map((source) => {
146
- if (typeof source === "object" && "urls" in source) {
155
+ const normalized = normalizeSourceInput(source);
156
+ if ("urls" in normalized) {
147
157
  return {
148
158
  timeTakenMs: 0,
149
- ...source,
150
- urls: source.urls
159
+ ...normalized,
160
+ urls: normalized.urls
151
161
  };
152
162
  }
153
- if (source.fetch)
154
- return fetchDataSource(source, event);
163
+ if (normalized.fetch)
164
+ return fetchDataSource(normalized, event);
155
165
  return {
156
- ...source,
166
+ ...normalized,
157
167
  error: "Invalid source"
158
168
  };
159
169
  })
@@ -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)[];
@@ -357,7 +366,7 @@ export interface SitemapOutputHookCtx extends NitroBaseHook {
357
366
  }
358
367
  export interface SitemapSourcesHookCtx extends NitroBaseHook {
359
368
  sitemapName: string;
360
- sources: (SitemapSourceBase | SitemapSourceResolved)[];
369
+ sources: SitemapSourceInput[];
361
370
  }
362
371
  export type Changefreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
363
372
  export interface SitemapUrl {
@@ -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 {
@@ -11,4 +11,10 @@ export interface CreateFilterOptions {
11
11
  exclude?: (FilterInput | string | RegExp)[];
12
12
  }
13
13
  export declare function createPathFilter(options?: CreateFilterOptions): (loc: string) => boolean;
14
+ export interface PageMatch {
15
+ mappings: Record<string, string | false>;
16
+ paramSegments: string[];
17
+ }
18
+ export declare function findPageMapping(pathWithoutPrefix: string, pages: Record<string, Record<string, string | false>>): PageMatch | null;
19
+ export declare function applyDynamicParams(customPath: string, paramSegments: string[]): string;
14
20
  export declare function createFilter(options?: CreateFilterOptions): (path: string) => boolean;
@@ -58,6 +58,26 @@ export function createPathFilter(options = {}) {
58
58
  return urlFilter(path);
59
59
  };
60
60
  }
61
+ export function findPageMapping(pathWithoutPrefix, pages) {
62
+ const stripped = pathWithoutPrefix[0] === "/" ? pathWithoutPrefix.slice(1) : pathWithoutPrefix;
63
+ const pageKey = stripped.endsWith("/index") ? stripped.slice(0, -6) || "index" : stripped || "index";
64
+ if (pages[pageKey])
65
+ return { mappings: pages[pageKey], paramSegments: [] };
66
+ const sortedKeys = Object.keys(pages).sort((a, b) => b.length - a.length);
67
+ for (const key of sortedKeys) {
68
+ if (pageKey.startsWith(key + "/")) {
69
+ const paramPath = pageKey.slice(key.length + 1);
70
+ return { mappings: pages[key], paramSegments: paramPath.split("/") };
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+ export function applyDynamicParams(customPath, paramSegments) {
76
+ if (!paramSegments.length)
77
+ return customPath;
78
+ let i = 0;
79
+ return customPath.replace(/\[[^\]]+\]/g, () => paramSegments[i++] || "");
80
+ }
61
81
  export function createFilter(options = {}) {
62
82
  const include = options.include || [];
63
83
  const exclude = options.exclude || [];
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.1",
5
5
  "description": "Powerfully flexible XML Sitemaps that integrate seamlessly, for Nuxt.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -75,13 +75,14 @@
75
75
  },
76
76
  "devDependencies": {
77
77
  "@arethetypeswrong/cli": "^0.18.2",
78
- "@nuxt/content": "^3.9.0",
78
+ "@nuxt/content": "^3.10.0",
79
79
  "@nuxt/eslint-config": "^1.12.1",
80
80
  "@nuxt/module-builder": "^1.0.2",
81
- "@nuxt/test-utils": "^3.21.0",
82
- "@nuxt/ui": "^4.2.1",
81
+ "@nuxt/test-utils": "^3.22.0",
82
+ "@nuxt/ui": "^4.3.0",
83
83
  "@nuxtjs/i18n": "^10.2.1",
84
84
  "@nuxtjs/robots": "^5.6.7",
85
+ "@vue/test-utils": "^2.4.6",
85
86
  "better-sqlite3": "^12.5.0",
86
87
  "bumpp": "^10.3.2",
87
88
  "eslint": "^9.39.2",
@@ -89,11 +90,11 @@
89
90
  "execa": "^9.6.1",
90
91
  "happy-dom": "^20.0.11",
91
92
  "nuxt": "^4.2.2",
92
- "nuxt-i18n-micro": "^2.15.2",
93
+ "nuxt-i18n-micro": "^2.16.3",
93
94
  "typescript": "^5.9.3",
94
95
  "vitest": "3.2.4",
95
- "vue-tsc": "^3.1.8",
96
- "@nuxtjs/sitemap": "7.4.11"
96
+ "vue-tsc": "^3.2.2",
97
+ "@nuxtjs/sitemap": "7.5.1"
97
98
  },
98
99
  "scripts": {
99
100
  "lint": "eslint .",