@nuxtjs/sitemap 7.4.5 → 7.4.8

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 (84) hide show
  1. package/README.md +1 -1
  2. package/dist/client/200.html +1 -0
  3. package/dist/client/404.html +1 -0
  4. package/dist/client/__sitemap__/style.xsl +190 -0
  5. package/dist/client/_nuxt/BD3NqoLn.js +1 -0
  6. package/dist/client/_nuxt/CGPPaMu7.js +1 -0
  7. package/dist/client/_nuxt/CVO1_9PV.js +1 -0
  8. package/dist/client/_nuxt/Cp-IABpG.js +1 -0
  9. package/dist/client/_nuxt/D0r3Knsf.js +1 -0
  10. package/dist/client/_nuxt/HdjdXzAy.js +157 -0
  11. package/dist/client/_nuxt/builds/latest.json +1 -0
  12. package/dist/client/_nuxt/builds/meta/61bb4b51-7d01-424f-91fb-0e4ca01f7b22.json +1 -0
  13. package/dist/client/_nuxt/entry.Ci1pP-eR.css +1 -0
  14. package/dist/client/_nuxt/error-404.i2ufnNET.css +1 -0
  15. package/dist/client/_nuxt/error-500.CIrNTbBE.css +1 -0
  16. package/dist/client/index.html +1 -0
  17. package/dist/client/sitemap.xml +7 -0
  18. package/dist/content.d.mts +232 -0
  19. package/dist/content.d.ts +232 -0
  20. package/dist/content.mjs +45 -0
  21. package/dist/module.d.mts +10 -0
  22. package/dist/module.d.ts +10 -0
  23. package/dist/module.json +12 -0
  24. package/dist/module.mjs +1270 -0
  25. package/dist/runtime/server/composables/asSitemapUrl.d.ts +2 -0
  26. package/dist/runtime/server/composables/asSitemapUrl.js +3 -0
  27. package/dist/runtime/server/composables/defineSitemapEventHandler.d.ts +4 -0
  28. package/dist/runtime/server/composables/defineSitemapEventHandler.js +2 -0
  29. package/dist/runtime/server/content-compat.d.ts +1 -0
  30. package/dist/runtime/server/content-compat.js +2 -0
  31. package/dist/runtime/server/kit.d.ts +3 -0
  32. package/dist/runtime/server/kit.js +25 -0
  33. package/dist/runtime/server/plugins/compression.d.ts +2 -0
  34. package/dist/runtime/server/plugins/compression.js +8 -0
  35. package/dist/runtime/server/plugins/nuxt-content-v2.d.ts +2 -0
  36. package/dist/runtime/server/plugins/nuxt-content-v2.js +39 -0
  37. package/dist/runtime/server/plugins/warm-up.d.ts +2 -0
  38. package/dist/runtime/server/plugins/warm-up.js +39 -0
  39. package/dist/runtime/server/robots-polyfill/getPathRobotConfig.d.ts +5 -0
  40. package/dist/runtime/server/robots-polyfill/getPathRobotConfig.js +3 -0
  41. package/dist/runtime/server/routes/__sitemap__/debug.d.ts +45 -0
  42. package/dist/runtime/server/routes/__sitemap__/debug.js +31 -0
  43. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v2.d.ts +2 -0
  44. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v2.js +6 -0
  45. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.d.ts +2 -0
  46. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.js +24 -0
  47. package/dist/runtime/server/routes/sitemap/[sitemap].xml.d.ts +2 -0
  48. package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +50 -0
  49. package/dist/runtime/server/routes/sitemap.xml.d.ts +2 -0
  50. package/dist/runtime/server/routes/sitemap.xml.js +13 -0
  51. package/dist/runtime/server/routes/sitemap.xsl.d.ts +2 -0
  52. package/dist/runtime/server/routes/sitemap.xsl.js +255 -0
  53. package/dist/runtime/server/routes/sitemap_index.xml.d.ts +2 -0
  54. package/dist/runtime/server/routes/sitemap_index.xml.js +42 -0
  55. package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +13 -0
  56. package/dist/runtime/server/sitemap/builder/sitemap-index.js +197 -0
  57. package/dist/runtime/server/sitemap/builder/sitemap.d.ts +16 -0
  58. package/dist/runtime/server/sitemap/builder/sitemap.js +243 -0
  59. package/dist/runtime/server/sitemap/builder/xml.d.ts +6 -0
  60. package/dist/runtime/server/sitemap/builder/xml.js +197 -0
  61. package/dist/runtime/server/sitemap/nitro.d.ts +4 -0
  62. package/dist/runtime/server/sitemap/nitro.js +144 -0
  63. package/dist/runtime/server/sitemap/urlset/normalise.d.ts +6 -0
  64. package/dist/runtime/server/sitemap/urlset/normalise.js +137 -0
  65. package/dist/runtime/server/sitemap/urlset/sort.d.ts +2 -0
  66. package/dist/runtime/server/sitemap/urlset/sort.js +13 -0
  67. package/dist/runtime/server/sitemap/urlset/sources.d.ts +6 -0
  68. package/dist/runtime/server/sitemap/urlset/sources.js +159 -0
  69. package/dist/runtime/server/sitemap/utils/chunk.d.ts +10 -0
  70. package/dist/runtime/server/sitemap/utils/chunk.js +67 -0
  71. package/dist/runtime/server/tsconfig.json +3 -0
  72. package/dist/runtime/server/utils.d.ts +5 -0
  73. package/dist/runtime/server/utils.js +16 -0
  74. package/dist/runtime/types.d.ts +458 -0
  75. package/dist/runtime/types.js +0 -0
  76. package/dist/runtime/utils-pure.d.ts +14 -0
  77. package/dist/runtime/utils-pure.js +85 -0
  78. package/dist/shared/sitemap.Bj0OAEtK.mjs +223 -0
  79. package/dist/types.d.mts +5 -0
  80. package/dist/utils.d.mts +28 -0
  81. package/dist/utils.d.ts +28 -0
  82. package/dist/utils.mjs +368 -0
  83. package/package.json +25 -25
  84. package/virtual.d.ts +4 -0
@@ -0,0 +1,197 @@
1
+ import { withQuery } from "ufo";
2
+ import { xmlEscape } from "../../utils.js";
3
+ export function escapeValueForXml(value) {
4
+ if (value === true || value === false)
5
+ return value ? "yes" : "no";
6
+ return xmlEscape(String(value));
7
+ }
8
+ const URLSET_OPENING_TAG = '<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">';
9
+ function buildUrlXml(url) {
10
+ const capacity = 50;
11
+ const parts = Array.from({ length: capacity });
12
+ let partIndex = 0;
13
+ parts[partIndex++] = " <url>";
14
+ if (url.loc) {
15
+ parts[partIndex++] = ` <loc>${escapeValueForXml(url.loc)}</loc>`;
16
+ }
17
+ if (url.lastmod) {
18
+ parts[partIndex++] = ` <lastmod>${url.lastmod}</lastmod>`;
19
+ }
20
+ if (url.changefreq) {
21
+ parts[partIndex++] = ` <changefreq>${url.changefreq}</changefreq>`;
22
+ }
23
+ if (url.priority !== void 0) {
24
+ const priorityValue = Number.parseFloat(String(url.priority));
25
+ const formattedPriority = priorityValue % 1 === 0 ? String(priorityValue) : priorityValue.toFixed(1);
26
+ parts[partIndex++] = ` <priority>${formattedPriority}</priority>`;
27
+ }
28
+ const keys = Object.keys(url).filter((k) => !k.startsWith("_") && !["loc", "lastmod", "changefreq", "priority"].includes(k));
29
+ for (const key of keys) {
30
+ const value = url[key];
31
+ if (value === void 0 || value === null) continue;
32
+ switch (key) {
33
+ case "alternatives":
34
+ if (Array.isArray(value) && value.length > 0) {
35
+ for (const alt of value) {
36
+ const attrs = Object.entries(alt).map(([k, v]) => `${k}="${escapeValueForXml(v)}"`).join(" ");
37
+ parts[partIndex++] = ` <xhtml:link rel="alternate" ${attrs} />`;
38
+ }
39
+ }
40
+ break;
41
+ case "images":
42
+ if (Array.isArray(value) && value.length > 0) {
43
+ for (const img of value) {
44
+ parts[partIndex++] = " <image:image>";
45
+ parts[partIndex++] = ` <image:loc>${escapeValueForXml(img.loc)}</image:loc>`;
46
+ if (img.title) parts[partIndex++] = ` <image:title>${escapeValueForXml(img.title)}</image:title>`;
47
+ if (img.caption) parts[partIndex++] = ` <image:caption>${escapeValueForXml(img.caption)}</image:caption>`;
48
+ if (img.geo_location) parts[partIndex++] = ` <image:geo_location>${escapeValueForXml(img.geo_location)}</image:geo_location>`;
49
+ if (img.license) parts[partIndex++] = ` <image:license>${escapeValueForXml(img.license)}</image:license>`;
50
+ parts[partIndex++] = " </image:image>";
51
+ }
52
+ }
53
+ break;
54
+ case "videos":
55
+ if (Array.isArray(value) && value.length > 0) {
56
+ for (const video of value) {
57
+ parts[partIndex++] = " <video:video>";
58
+ parts[partIndex++] = ` <video:title>${escapeValueForXml(video.title)}</video:title>`;
59
+ if (video.thumbnail_loc) {
60
+ parts[partIndex++] = ` <video:thumbnail_loc>${escapeValueForXml(video.thumbnail_loc)}</video:thumbnail_loc>`;
61
+ }
62
+ parts[partIndex++] = ` <video:description>${escapeValueForXml(video.description)}</video:description>`;
63
+ if (video.content_loc) {
64
+ parts[partIndex++] = ` <video:content_loc>${escapeValueForXml(video.content_loc)}</video:content_loc>`;
65
+ }
66
+ if (video.player_loc) {
67
+ const attrs = video.player_loc.allow_embed ? ' allow_embed="yes"' : "";
68
+ const autoplay = video.player_loc.autoplay ? ' autoplay="yes"' : "";
69
+ parts[partIndex++] = ` <video:player_loc${attrs}${autoplay}>${escapeValueForXml(video.player_loc)}</video:player_loc>`;
70
+ }
71
+ if (video.duration !== void 0) {
72
+ parts[partIndex++] = ` <video:duration>${video.duration}</video:duration>`;
73
+ }
74
+ if (video.expiration_date) {
75
+ parts[partIndex++] = ` <video:expiration_date>${video.expiration_date}</video:expiration_date>`;
76
+ }
77
+ if (video.rating !== void 0) {
78
+ parts[partIndex++] = ` <video:rating>${video.rating}</video:rating>`;
79
+ }
80
+ if (video.view_count !== void 0) {
81
+ parts[partIndex++] = ` <video:view_count>${video.view_count}</video:view_count>`;
82
+ }
83
+ if (video.publication_date) {
84
+ parts[partIndex++] = ` <video:publication_date>${video.publication_date}</video:publication_date>`;
85
+ }
86
+ if (video.family_friendly !== void 0) {
87
+ parts[partIndex++] = ` <video:family_friendly>${video.family_friendly === "yes" || video.family_friendly === true ? "yes" : "no"}</video:family_friendly>`;
88
+ }
89
+ if (video.restriction) {
90
+ const relationship = video.restriction.relationship || "allow";
91
+ parts[partIndex++] = ` <video:restriction relationship="${relationship}">${escapeValueForXml(video.restriction.restriction)}</video:restriction>`;
92
+ }
93
+ if (video.platform) {
94
+ const relationship = video.platform.relationship || "allow";
95
+ parts[partIndex++] = ` <video:platform relationship="${relationship}">${escapeValueForXml(video.platform.platform)}</video:platform>`;
96
+ }
97
+ if (video.requires_subscription !== void 0) {
98
+ parts[partIndex++] = ` <video:requires_subscription>${video.requires_subscription === "yes" || video.requires_subscription === true ? "yes" : "no"}</video:requires_subscription>`;
99
+ }
100
+ if (video.price) {
101
+ const prices = Array.isArray(video.price) ? video.price : [video.price];
102
+ for (const price of prices) {
103
+ const attrs = [];
104
+ if (price.currency) attrs.push(`currency="${price.currency}"`);
105
+ if (price.type) attrs.push(`type="${price.type}"`);
106
+ const attrsStr = attrs.length > 0 ? " " + attrs.join(" ") : "";
107
+ parts[partIndex++] = ` <video:price${attrsStr}>${escapeValueForXml(price.price)}</video:price>`;
108
+ }
109
+ }
110
+ if (video.uploader) {
111
+ const info = video.uploader.info ? ` info="${escapeValueForXml(video.uploader.info)}"` : "";
112
+ parts[partIndex++] = ` <video:uploader${info}>${escapeValueForXml(video.uploader.uploader)}</video:uploader>`;
113
+ }
114
+ if (video.live !== void 0) {
115
+ parts[partIndex++] = ` <video:live>${video.live === "yes" || video.live === true ? "yes" : "no"}</video:live>`;
116
+ }
117
+ if (video.tag) {
118
+ const tags = Array.isArray(video.tag) ? video.tag : [video.tag];
119
+ for (const tag of tags) {
120
+ parts[partIndex++] = ` <video:tag>${escapeValueForXml(tag)}</video:tag>`;
121
+ }
122
+ }
123
+ if (video.category) {
124
+ parts[partIndex++] = ` <video:category>${escapeValueForXml(video.category)}</video:category>`;
125
+ }
126
+ if (video.gallery_loc) {
127
+ const title = video.gallery_loc.title ? ` title="${escapeValueForXml(video.gallery_loc.title)}"` : "";
128
+ parts[partIndex++] = ` <video:gallery_loc${title}>${escapeValueForXml(video.gallery_loc)}</video:gallery_loc>`;
129
+ }
130
+ parts[partIndex++] = " </video:video>";
131
+ }
132
+ }
133
+ break;
134
+ case "news":
135
+ if (value) {
136
+ parts[partIndex++] = " <news:news>";
137
+ parts[partIndex++] = " <news:publication>";
138
+ parts[partIndex++] = ` <news:name>${escapeValueForXml(value.publication.name)}</news:name>`;
139
+ parts[partIndex++] = ` <news:language>${escapeValueForXml(value.publication.language)}</news:language>`;
140
+ parts[partIndex++] = " </news:publication>";
141
+ if (value.title) {
142
+ parts[partIndex++] = ` <news:title>${escapeValueForXml(value.title)}</news:title>`;
143
+ }
144
+ if (value.publication_date) {
145
+ parts[partIndex++] = ` <news:publication_date>${value.publication_date}</news:publication_date>`;
146
+ }
147
+ if (value.access) {
148
+ parts[partIndex++] = ` <news:access>${value.access}</news:access>`;
149
+ }
150
+ if (value.genres) {
151
+ parts[partIndex++] = ` <news:genres>${escapeValueForXml(value.genres)}</news:genres>`;
152
+ }
153
+ if (value.keywords) {
154
+ parts[partIndex++] = ` <news:keywords>${escapeValueForXml(value.keywords)}</news:keywords>`;
155
+ }
156
+ if (value.stock_tickers) {
157
+ parts[partIndex++] = ` <news:stock_tickers>${escapeValueForXml(value.stock_tickers)}</news:stock_tickers>`;
158
+ }
159
+ parts[partIndex++] = " </news:news>";
160
+ }
161
+ break;
162
+ }
163
+ }
164
+ parts[partIndex++] = " </url>";
165
+ return parts.slice(0, partIndex).join("\n");
166
+ }
167
+ export function urlsToXml(urls, resolvers, { version, xsl, credits, minify }, errorInfo) {
168
+ const estimatedSize = urls.length + 5;
169
+ const xmlParts = Array.from({ length: estimatedSize });
170
+ let partIndex = 0;
171
+ let xslHref = xsl ? resolvers.relativeBaseUrlResolver(xsl) : false;
172
+ if (xslHref && errorInfo && errorInfo.messages.length > 0) {
173
+ xslHref = withQuery(xslHref, {
174
+ errors: "true",
175
+ error_messages: errorInfo.messages,
176
+ error_urls: errorInfo.urls
177
+ });
178
+ }
179
+ if (xslHref) {
180
+ xmlParts[partIndex++] = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="${escapeValueForXml(xslHref)}"?>`;
181
+ } else {
182
+ xmlParts[partIndex++] = '<?xml version="1.0" encoding="UTF-8"?>';
183
+ }
184
+ xmlParts[partIndex++] = URLSET_OPENING_TAG;
185
+ for (const url of urls) {
186
+ xmlParts[partIndex++] = buildUrlXml(url);
187
+ }
188
+ xmlParts[partIndex++] = "</urlset>";
189
+ if (credits) {
190
+ xmlParts[partIndex++] = `<!-- XML Sitemap generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`;
191
+ }
192
+ const xmlContent = xmlParts.slice(0, partIndex);
193
+ if (minify) {
194
+ return xmlContent.join("").replace(/(?<!<[^>]*)\s(?![^<]*>)/g, "");
195
+ }
196
+ return xmlContent.join("\n");
197
+ }
@@ -0,0 +1,4 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapDefinition } from '../../types.js';
3
+ export declare function useNitroUrlResolvers(e: H3Event): NitroUrlResolvers;
4
+ export declare function createSitemap(event: H3Event, definition: SitemapDefinition, runtimeConfig: ModuleRuntimeConfig): Promise<string>;
@@ -0,0 +1,144 @@
1
+ import { getQuery, setHeader, createError, getHeader } from "h3";
2
+ import { fixSlashes } from "nuxt-site-config/urls";
3
+ import { defu } from "defu";
4
+ import { useNitroApp, defineCachedFunction } from "nitropack/runtime";
5
+ import { logger, mergeOnKey, splitForLocales } from "../../utils-pure.js";
6
+ import { createNitroRouteRuleMatcher } from "../kit.js";
7
+ import { buildSitemapUrls, urlsToXml } from "./builder/sitemap.js";
8
+ import { normaliseEntry, preNormalizeEntry } from "./urlset/normalise.js";
9
+ import { sortInPlace } from "./urlset/sort.js";
10
+ import { getPathRobotConfig } from "#internal/nuxt-robots/getPathRobotConfig";
11
+ import { useSiteConfig } from "#site-config/server/composables/useSiteConfig";
12
+ import { createSitePathResolver } from "#site-config/server/composables/utils";
13
+ export function useNitroUrlResolvers(e) {
14
+ const canonicalQuery = getQuery(e).canonical;
15
+ const isShowingCanonical = typeof canonicalQuery !== "undefined" && canonicalQuery !== "false";
16
+ const siteConfig = useSiteConfig(e);
17
+ return {
18
+ event: e,
19
+ fixSlashes: (path) => fixSlashes(siteConfig.trailingSlash, path),
20
+ // we need these as they depend on the nitro event
21
+ canonicalUrlResolver: createSitePathResolver(e, {
22
+ canonical: isShowingCanonical || !import.meta.dev,
23
+ absolute: true,
24
+ withBase: true
25
+ }),
26
+ relativeBaseUrlResolver: createSitePathResolver(e, { absolute: false, withBase: true })
27
+ };
28
+ }
29
+ async function buildSitemapXml(event, definition, resolvers, runtimeConfig) {
30
+ const { sitemapName } = definition;
31
+ const nitro = useNitroApp();
32
+ if (import.meta.prerender) {
33
+ const config = useSiteConfig(event);
34
+ if (!config.url && !nitro._sitemapWarned) {
35
+ nitro._sitemapWarned = true;
36
+ logger.error("Sitemap Site URL missing!");
37
+ logger.info("To fix this please add `{ site: { url: 'site.com' } }` to your Nuxt config or a `NUXT_PUBLIC_SITE_URL=site.com` to your .env. Learn more at https://nuxtseo.com/site-config/getting-started/how-it-works");
38
+ throw new createError({
39
+ statusMessage: "You must provide a site URL to prerender a sitemap.",
40
+ statusCode: 500
41
+ });
42
+ }
43
+ }
44
+ const { urls: sitemapUrls, failedSources } = await buildSitemapUrls(definition, resolvers, runtimeConfig, nitro);
45
+ const routeRuleMatcher = createNitroRouteRuleMatcher();
46
+ const { autoI18n } = runtimeConfig;
47
+ let validCount = 0;
48
+ for (let i = 0; i < sitemapUrls.length; i++) {
49
+ const u = sitemapUrls[i];
50
+ const path = u._path?.pathname || u.loc;
51
+ if (!getPathRobotConfig(event, { path, skipSiteIndexable: true }).indexable)
52
+ continue;
53
+ let routeRules = routeRuleMatcher(path);
54
+ if (autoI18n?.locales && autoI18n?.strategy !== "no_prefix") {
55
+ const match = splitForLocales(path, autoI18n.locales.map((l) => l.code));
56
+ const pathWithoutPrefix = match[1];
57
+ if (pathWithoutPrefix && pathWithoutPrefix !== path)
58
+ routeRules = defu(routeRules, routeRuleMatcher(pathWithoutPrefix));
59
+ }
60
+ if (routeRules.sitemap === false)
61
+ continue;
62
+ if (typeof routeRules.robots !== "undefined" && !routeRules.robots)
63
+ continue;
64
+ const hasRobotsDisabled = Object.entries(routeRules.headers || {}).some(([name, value]) => name.toLowerCase() === "x-robots-tag" && value.toLowerCase().includes("noindex"));
65
+ if (routeRules.redirect || hasRobotsDisabled)
66
+ continue;
67
+ sitemapUrls[validCount++] = routeRules.sitemap ? defu(u, routeRules.sitemap) : u;
68
+ }
69
+ sitemapUrls.length = validCount;
70
+ const locSize = sitemapUrls.length;
71
+ const resolvedCtx = {
72
+ urls: sitemapUrls,
73
+ sitemapName,
74
+ event
75
+ };
76
+ await nitro.hooks.callHook("sitemap:resolved", resolvedCtx);
77
+ if (resolvedCtx.urls.length !== locSize) {
78
+ resolvedCtx.urls = resolvedCtx.urls.map((e) => preNormalizeEntry(e, resolvers));
79
+ }
80
+ const maybeSort = (urls2) => runtimeConfig.sortEntries ? sortInPlace(urls2) : urls2;
81
+ const normalizedPreDedupe = resolvedCtx.urls.map((e) => normaliseEntry(e, definition.defaults, resolvers));
82
+ const urls = maybeSort(mergeOnKey(normalizedPreDedupe, "_key").map((e) => normaliseEntry(e, definition.defaults, resolvers)));
83
+ if (definition._isChunking && definition.sitemapName.includes("-")) {
84
+ const parts = definition.sitemapName.split("-");
85
+ const lastPart = parts.pop();
86
+ if (!Number.isNaN(Number(lastPart))) {
87
+ const chunkIndex = Number(lastPart);
88
+ const baseSitemapName = parts.join("-");
89
+ if (urls.length === 0 && chunkIndex > 0) {
90
+ throw createError({
91
+ statusCode: 404,
92
+ message: `Sitemap chunk ${chunkIndex} for "${baseSitemapName}" does not exist.`
93
+ });
94
+ }
95
+ }
96
+ }
97
+ const errorInfo = failedSources.length > 0 ? {
98
+ messages: failedSources.map((f) => f.error),
99
+ urls: failedSources.map((f) => f.url)
100
+ } : void 0;
101
+ const sitemap = urlsToXml(urls, resolvers, runtimeConfig, errorInfo);
102
+ const ctx = { sitemap, sitemapName, event };
103
+ await nitro.hooks.callHook("sitemap:output", ctx);
104
+ return ctx.sitemap;
105
+ }
106
+ const buildSitemapXmlCached = defineCachedFunction(
107
+ buildSitemapXml,
108
+ {
109
+ name: "sitemap:xml",
110
+ group: "sitemap",
111
+ maxAge: 60 * 10,
112
+ // Default 10 minutes
113
+ base: "sitemap",
114
+ // Use the sitemap storage
115
+ getKey: (event, definition) => {
116
+ const host = getHeader(event, "host") || getHeader(event, "x-forwarded-host") || "";
117
+ const proto = getHeader(event, "x-forwarded-proto") || "https";
118
+ const sitemapName = definition.sitemapName || "default";
119
+ return `${sitemapName}-${proto}-${host}`;
120
+ },
121
+ swr: true
122
+ // Enable stale-while-revalidate
123
+ }
124
+ );
125
+ export async function createSitemap(event, definition, runtimeConfig) {
126
+ const resolvers = useNitroUrlResolvers(event);
127
+ const shouldCache = !import.meta.dev && typeof runtimeConfig.cacheMaxAgeSeconds === "number" && runtimeConfig.cacheMaxAgeSeconds > 0;
128
+ const xml = shouldCache ? await buildSitemapXmlCached(event, definition, resolvers, runtimeConfig) : await buildSitemapXml(event, definition, resolvers, runtimeConfig);
129
+ setHeader(event, "Content-Type", "text/xml; charset=UTF-8");
130
+ if (runtimeConfig.cacheMaxAgeSeconds) {
131
+ setHeader(event, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
132
+ const now = /* @__PURE__ */ new Date();
133
+ setHeader(event, "X-Sitemap-Generated", now.toISOString());
134
+ setHeader(event, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
135
+ const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
136
+ setHeader(event, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
137
+ const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
138
+ setHeader(event, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
139
+ } else {
140
+ setHeader(event, "Cache-Control", `no-cache, no-store`);
141
+ }
142
+ event.context._isSitemap = true;
143
+ return xml;
144
+ }
@@ -0,0 +1,6 @@
1
+ import type { NitroUrlResolvers, ResolvedSitemapUrl, SitemapUrl } from '../../../types.js';
2
+ export declare function preNormalizeEntry(_e: SitemapUrl | string, resolvers?: NitroUrlResolvers): ResolvedSitemapUrl;
3
+ export declare function isEncoded(url: string): boolean;
4
+ export declare function normaliseEntry(_e: ResolvedSitemapUrl, defaults: Omit<SitemapUrl, 'loc'>, resolvers?: NitroUrlResolvers): ResolvedSitemapUrl;
5
+ export declare function isValidW3CDate(d: string): boolean;
6
+ export declare function normaliseDate(date: string | Date): string;
@@ -0,0 +1,137 @@
1
+ import {
2
+ encodePath,
3
+ hasProtocol,
4
+ parsePath,
5
+ parseQuery,
6
+ parseURL,
7
+ stringifyParsedURL,
8
+ stringifyQuery,
9
+ withoutTrailingSlash
10
+ } from "ufo";
11
+ import { defu } from "defu";
12
+ import { mergeOnKey } from "../../../utils-pure.js";
13
+ function resolve(s, resolvers) {
14
+ if (typeof s === "undefined" || !resolvers)
15
+ return s;
16
+ s = typeof s === "string" ? s : s.toString();
17
+ if (hasProtocol(s, { acceptRelative: true, strict: false }))
18
+ return resolvers.fixSlashes(s);
19
+ return resolvers.canonicalUrlResolver(s);
20
+ }
21
+ function removeTrailingSlash(s) {
22
+ return s.replace(/\/(\?|#|$)/, "$1");
23
+ }
24
+ export function preNormalizeEntry(_e, resolvers) {
25
+ const e = typeof _e === "string" ? { loc: _e } : { ..._e };
26
+ if (e.url && !e.loc) {
27
+ e.loc = e.url;
28
+ delete e.url;
29
+ }
30
+ if (typeof e.loc !== "string") {
31
+ e.loc = "";
32
+ }
33
+ e.loc = removeTrailingSlash(e.loc);
34
+ e._abs = hasProtocol(e.loc, { acceptRelative: false, strict: false });
35
+ try {
36
+ e._path = e._abs ? parseURL(e.loc) : parsePath(e.loc);
37
+ } catch (e2) {
38
+ e2._path = null;
39
+ }
40
+ if (e._path) {
41
+ const query = parseQuery(e._path.search);
42
+ const qs = stringifyQuery(query);
43
+ e._relativeLoc = `${encodePath(e._path?.pathname)}${qs.length ? `?${qs}` : ""}`;
44
+ if (e._path.host) {
45
+ e.loc = stringifyParsedURL(e._path);
46
+ } else {
47
+ e.loc = e._relativeLoc;
48
+ }
49
+ } else if (!isEncoded(e.loc)) {
50
+ e.loc = encodeURI(e.loc);
51
+ }
52
+ if (e.loc === "")
53
+ e.loc = `/`;
54
+ e.loc = resolve(e.loc, resolvers);
55
+ e._key = `${e._sitemap || ""}${withoutTrailingSlash(e.loc)}`;
56
+ return e;
57
+ }
58
+ export function isEncoded(url) {
59
+ try {
60
+ return url !== decodeURIComponent(url);
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+ export function normaliseEntry(_e, defaults, resolvers) {
66
+ const e = defu(_e, defaults);
67
+ if (e.lastmod) {
68
+ const date = normaliseDate(e.lastmod);
69
+ if (date)
70
+ e.lastmod = date;
71
+ else
72
+ delete e.lastmod;
73
+ }
74
+ if (!e.lastmod)
75
+ delete e.lastmod;
76
+ e.loc = resolve(e.loc, resolvers);
77
+ if (e.alternatives) {
78
+ const alternatives = e.alternatives.map((a) => ({ ...a }));
79
+ for (let i = 0; i < alternatives.length; i++) {
80
+ const alt = alternatives[i];
81
+ if (typeof alt.href === "string") {
82
+ alt.href = resolve(alt.href, resolvers);
83
+ } else if (typeof alt.href === "object" && alt.href) {
84
+ alt.href = resolve(alt.href.href, resolvers);
85
+ }
86
+ }
87
+ e.alternatives = mergeOnKey(alternatives, "hreflang");
88
+ }
89
+ if (e.images) {
90
+ const images = e.images.map((i) => ({ ...i }));
91
+ for (let i = 0; i < images.length; i++) {
92
+ images[i].loc = resolve(images[i].loc, resolvers);
93
+ }
94
+ e.images = mergeOnKey(images, "loc");
95
+ }
96
+ if (e.videos) {
97
+ const videos = e.videos.map((v) => ({ ...v }));
98
+ for (let i = 0; i < videos.length; i++) {
99
+ if (videos[i].content_loc) {
100
+ videos[i].content_loc = resolve(videos[i].content_loc, resolvers);
101
+ }
102
+ }
103
+ e.videos = mergeOnKey(videos, "content_loc");
104
+ }
105
+ return e;
106
+ }
107
+ const IS_VALID_W3C_DATE = [
108
+ /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/,
109
+ /^\d{4}-[01]\d-[0-3]\d$/,
110
+ /^\d{4}-[01]\d$/,
111
+ /^\d{4}$/
112
+ ];
113
+ export function isValidW3CDate(d) {
114
+ return IS_VALID_W3C_DATE.some((r) => r.test(d));
115
+ }
116
+ export function normaliseDate(d) {
117
+ if (typeof d === "string") {
118
+ if (d.includes("T")) {
119
+ const t = d.split("T")[1];
120
+ if (!t.includes("+") && !t.includes("-") && !t.includes("Z")) {
121
+ d += "Z";
122
+ }
123
+ }
124
+ if (!isValidW3CDate(d))
125
+ return false;
126
+ d = new Date(d);
127
+ d.setMilliseconds(0);
128
+ if (Number.isNaN(d.getTime()))
129
+ return false;
130
+ }
131
+ const z = (n) => `0${n}`.slice(-2);
132
+ const date = `${d.getUTCFullYear()}-${z(d.getUTCMonth() + 1)}-${z(d.getUTCDate())}`;
133
+ if (d.getUTCHours() > 0 || d.getUTCMinutes() > 0 || d.getUTCSeconds() > 0) {
134
+ return `${date}T${z(d.getUTCHours())}:${z(d.getUTCMinutes())}:${z(d.getUTCSeconds())}Z`;
135
+ }
136
+ return date;
137
+ }
@@ -0,0 +1,2 @@
1
+ import type { ResolvedSitemapUrl, SitemapUrlInput } from '../../../types.js';
2
+ export declare function sortInPlace<T extends SitemapUrlInput[] | ResolvedSitemapUrl[]>(urls: T): T;
@@ -0,0 +1,13 @@
1
+ export function sortInPlace(urls) {
2
+ urls.sort((a, b) => {
3
+ const aLoc = typeof a === "string" ? a : a.loc;
4
+ const bLoc = typeof b === "string" ? b : b.loc;
5
+ const aSegments = aLoc.split("/").length;
6
+ const bSegments = bLoc.split("/").length;
7
+ if (aSegments !== bSegments) {
8
+ return aSegments - bSegments;
9
+ }
10
+ return aLoc.localeCompare(bLoc, void 0, { numeric: true });
11
+ });
12
+ return urls;
13
+ }
@@ -0,0 +1,6 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { ModuleRuntimeConfig, SitemapSourceBase, SitemapSourceResolved } from '../../../types.js';
3
+ export declare function fetchDataSource(input: SitemapSourceBase | SitemapSourceResolved, event?: H3Event): Promise<SitemapSourceResolved>;
4
+ export declare function globalSitemapSources(): Promise<any>;
5
+ export declare function childSitemapSources(definition: ModuleRuntimeConfig['sitemaps'][string]): Promise<any>;
6
+ export declare function resolveSitemapSources(sources: (SitemapSourceBase | SitemapSourceResolved)[], event?: H3Event): Promise<SitemapSourceResolved[]>;
@@ -0,0 +1,159 @@
1
+ import { getRequestHost } from "h3";
2
+ import { defu } from "defu";
3
+ import { parseURL } from "ufo";
4
+ import { logger } from "../../../utils-pure.js";
5
+ import { parseSitemapXml } from "@nuxtjs/sitemap/utils";
6
+ async function tryFetchWithFallback(url, options, event) {
7
+ const isExternalUrl = !url.startsWith("/");
8
+ if (isExternalUrl) {
9
+ const strategies = [
10
+ // Strategy 1: Use globalThis.$fetch (original approach)
11
+ () => globalThis.$fetch(url, options),
12
+ // Strategy 2: If event is available, try using event context even for external URLs
13
+ event ? () => event.$fetch(url, options) : null,
14
+ // Strategy 3: Use native fetch as last resort
15
+ () => $fetch(url, options)
16
+ ].filter(Boolean);
17
+ let lastError = null;
18
+ for (const strategy of strategies) {
19
+ try {
20
+ return await strategy();
21
+ } catch (error) {
22
+ lastError = error;
23
+ continue;
24
+ }
25
+ }
26
+ throw lastError;
27
+ }
28
+ const fetchContainer = url.startsWith("/") && event ? event : globalThis;
29
+ return await fetchContainer.$fetch(url, options);
30
+ }
31
+ export async function fetchDataSource(input, event) {
32
+ const context = typeof input.context === "string" ? { name: input.context } : input.context || { name: "fetch" };
33
+ const url = typeof input.fetch === "string" ? input.fetch : input.fetch[0];
34
+ const options = typeof input.fetch === "string" ? {} : input.fetch[1];
35
+ const start = Date.now();
36
+ const isExternalUrl = !url.startsWith("/");
37
+ const timeout = isExternalUrl ? 1e4 : options.timeout || 5e3;
38
+ const timeoutController = new AbortController();
39
+ const abortRequestTimeout = setTimeout(() => timeoutController.abort(), timeout);
40
+ try {
41
+ let isMaybeErrorResponse = false;
42
+ const isXmlRequest = parseURL(url).pathname.endsWith(".xml");
43
+ const mergedHeaders = defu(
44
+ options?.headers,
45
+ {
46
+ Accept: isXmlRequest ? "text/xml" : "application/json"
47
+ },
48
+ event ? { host: getRequestHost(event, { xForwardedHost: true }) } : {}
49
+ );
50
+ const fetchOptions = {
51
+ ...options,
52
+ responseType: isXmlRequest ? "text" : "json",
53
+ signal: timeoutController.signal,
54
+ headers: mergedHeaders,
55
+ // Use ofetch's built-in retry for external sources
56
+ ...isExternalUrl && {
57
+ retry: 2,
58
+ retryDelay: 200
59
+ },
60
+ // @ts-expect-error untyped
61
+ onResponse({ response }) {
62
+ if (typeof response._data === "string" && response._data.startsWith("<!DOCTYPE html>"))
63
+ isMaybeErrorResponse = true;
64
+ }
65
+ };
66
+ const res = await tryFetchWithFallback(url, fetchOptions, event);
67
+ const timeTakenMs = Date.now() - start;
68
+ if (isMaybeErrorResponse) {
69
+ return {
70
+ ...input,
71
+ context,
72
+ urls: [],
73
+ timeTakenMs,
74
+ error: "Received HTML response instead of JSON"
75
+ };
76
+ }
77
+ let urls = [];
78
+ if (typeof res === "object") {
79
+ urls = res.urls || res;
80
+ } else if (typeof res === "string" && parseURL(url).pathname.endsWith(".xml")) {
81
+ const result = await parseSitemapXml(res);
82
+ urls = result.urls;
83
+ }
84
+ return {
85
+ ...input,
86
+ context,
87
+ timeTakenMs,
88
+ urls
89
+ };
90
+ } catch (_err) {
91
+ const error = _err;
92
+ if (isExternalUrl) {
93
+ const errorInfo = {
94
+ url,
95
+ timeout,
96
+ error: error.message,
97
+ statusCode: error.response?.status,
98
+ statusText: error.response?.statusText,
99
+ method: options?.method || "GET"
100
+ };
101
+ logger.error("Failed to fetch external source.", errorInfo);
102
+ } else {
103
+ logger.error("Failed to fetch source.", { url, error: error.message });
104
+ }
105
+ return {
106
+ ...input,
107
+ context,
108
+ urls: [],
109
+ error: error.message,
110
+ _isFailure: true
111
+ // Mark as failure to prevent caching
112
+ };
113
+ } finally {
114
+ if (abortRequestTimeout) {
115
+ clearTimeout(abortRequestTimeout);
116
+ }
117
+ }
118
+ }
119
+ export async function globalSitemapSources() {
120
+ if (import.meta.prerender) {
121
+ const { readSourcesFromFilesystem } = await import("#sitemap-virtual/read-sources.mjs");
122
+ const sources = await readSourcesFromFilesystem("global-sources.json");
123
+ if (sources)
124
+ return sources;
125
+ }
126
+ const m = await import("#sitemap-virtual/global-sources.mjs");
127
+ return m.sources;
128
+ }
129
+ export async function childSitemapSources(definition) {
130
+ if (!definition?._hasSourceChunk)
131
+ return [];
132
+ if (import.meta.prerender) {
133
+ const { readSourcesFromFilesystem } = await import("#sitemap-virtual/read-sources.mjs");
134
+ const allSources = await readSourcesFromFilesystem("child-sources.json");
135
+ if (allSources)
136
+ return allSources[definition.sitemapName] || [];
137
+ }
138
+ const m = await import("#sitemap-virtual/child-sources.mjs");
139
+ return m.sources[definition.sitemapName] || [];
140
+ }
141
+ export async function resolveSitemapSources(sources, event) {
142
+ return (await Promise.all(
143
+ sources.map((source) => {
144
+ if (typeof source === "object" && "urls" in source) {
145
+ return {
146
+ timeTakenMs: 0,
147
+ ...source,
148
+ urls: source.urls
149
+ };
150
+ }
151
+ if (source.fetch)
152
+ return fetchDataSource(source, event);
153
+ return {
154
+ ...source,
155
+ error: "Invalid source"
156
+ };
157
+ })
158
+ )).flat();
159
+ }