@nuxtjs/sitemap 7.3.1 → 7.4.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 (37) hide show
  1. package/dist/client/200.html +7 -8
  2. package/dist/client/404.html +7 -8
  3. package/dist/client/_nuxt/ChEizYIG.js +172 -0
  4. package/dist/client/_nuxt/{CT3BV8Rj.js → SQMF8ibg.js} +1 -1
  5. package/dist/client/_nuxt/builds/latest.json +1 -1
  6. package/dist/client/_nuxt/builds/meta/048e1f4c-a575-4a94-bbab-d777afe4c585.json +1 -0
  7. package/dist/client/_nuxt/{5vafBU9X.js → cqJZcoo0.js} +1 -1
  8. package/dist/client/_nuxt/error-404.DljSaiyF.css +1 -0
  9. package/dist/client/_nuxt/error-500.DbX9fggi.css +1 -0
  10. package/dist/client/index.html +7 -8
  11. package/dist/module.cjs +28 -18
  12. package/dist/module.json +1 -1
  13. package/dist/module.mjs +28 -18
  14. package/dist/runtime/server/content-compat.d.ts +1 -0
  15. package/dist/runtime/server/content-compat.js +2 -0
  16. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.d.ts +1 -1
  17. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.js +4 -2
  18. package/dist/runtime/server/routes/sitemap.xsl.js +37 -13
  19. package/dist/runtime/server/routes/sitemap_index.xml.js +6 -2
  20. package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +11 -2
  21. package/dist/runtime/server/sitemap/builder/sitemap-index.js +23 -5
  22. package/dist/runtime/server/sitemap/builder/sitemap.d.ts +7 -1
  23. package/dist/runtime/server/sitemap/builder/sitemap.js +10 -5
  24. package/dist/runtime/server/sitemap/builder/xml.d.ts +4 -1
  25. package/dist/runtime/server/sitemap/builder/xml.js +13 -4
  26. package/dist/runtime/server/sitemap/nitro.js +7 -3
  27. package/dist/runtime/server/sitemap/urlset/normalise.js +4 -3
  28. package/dist/runtime/server/sitemap/urlset/sources.js +62 -17
  29. package/dist/runtime/server/utils.d.ts +1 -0
  30. package/dist/runtime/server/utils.js +3 -0
  31. package/dist/runtime/types.d.ts +1 -0
  32. package/package.json +11 -10
  33. package/dist/client/_nuxt/BIHI7g3E.js +0 -1
  34. package/dist/client/_nuxt/Bn78IMkz.js +0 -172
  35. package/dist/client/_nuxt/builds/meta/5ecca6e1-2b8a-4fc5-a128-35cbc27bf6d7.json +0 -1
  36. package/dist/client/_nuxt/error-404.D_zhMyJm.css +0 -1
  37. package/dist/client/_nuxt/error-500.rdOYVbxo.css +0 -1
@@ -41,7 +41,7 @@ async function buildSitemapXml(event, definition, resolvers, runtimeConfig) {
41
41
  });
42
42
  }
43
43
  }
44
- const sitemapUrls = await buildSitemapUrls(definition, resolvers, runtimeConfig, nitro);
44
+ const { urls: sitemapUrls, failedSources } = await buildSitemapUrls(definition, resolvers, runtimeConfig, nitro);
45
45
  const routeRuleMatcher = createNitroRouteRuleMatcher();
46
46
  const { autoI18n } = runtimeConfig;
47
47
  let validCount = 0;
@@ -94,7 +94,11 @@ async function buildSitemapXml(event, definition, resolvers, runtimeConfig) {
94
94
  }
95
95
  }
96
96
  }
97
- const sitemap = urlsToXml(urls, resolvers, runtimeConfig);
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);
98
102
  const ctx = { sitemap, sitemapName, event };
99
103
  await nitro.hooks.callHook("sitemap:output", ctx);
100
104
  return ctx.sitemap;
@@ -120,7 +124,7 @@ const buildSitemapXmlCached = defineCachedFunction(
120
124
  );
121
125
  export async function createSitemap(event, definition, runtimeConfig) {
122
126
  const resolvers = useNitroUrlResolvers(event);
123
- const shouldCache = !import.meta.dev && runtimeConfig.cacheMaxAgeSeconds > 0;
127
+ const shouldCache = !import.meta.dev && typeof runtimeConfig.cacheMaxAgeSeconds === "number" && runtimeConfig.cacheMaxAgeSeconds > 0;
124
128
  const xml = shouldCache ? await buildSitemapXmlCached(event, definition, resolvers, runtimeConfig) : await buildSitemapXml(event, definition, resolvers, runtimeConfig);
125
129
  setHeader(event, "Content-Type", "text/xml; charset=UTF-8");
126
130
  if (runtimeConfig.cacheMaxAgeSeconds) {
@@ -75,7 +75,7 @@ export function normaliseEntry(_e, defaults, resolvers) {
75
75
  delete e.lastmod;
76
76
  e.loc = resolve(e.loc, resolvers);
77
77
  if (e.alternatives) {
78
- const alternatives = e.alternatives;
78
+ const alternatives = e.alternatives.map((a) => ({ ...a }));
79
79
  for (let i = 0; i < alternatives.length; i++) {
80
80
  const alt = alternatives[i];
81
81
  if (typeof alt.href === "string") {
@@ -87,19 +87,20 @@ export function normaliseEntry(_e, defaults, resolvers) {
87
87
  e.alternatives = mergeOnKey(alternatives, "hreflang");
88
88
  }
89
89
  if (e.images) {
90
- const images = e.images;
90
+ const images = e.images.map((i) => ({ ...i }));
91
91
  for (let i = 0; i < images.length; i++) {
92
92
  images[i].loc = resolve(images[i].loc, resolvers);
93
93
  }
94
94
  e.images = mergeOnKey(images, "loc");
95
95
  }
96
96
  if (e.videos) {
97
- const videos = e.videos;
97
+ const videos = e.videos.map((v) => ({ ...v }));
98
98
  for (let i = 0; i < videos.length; i++) {
99
99
  if (videos[i].content_loc) {
100
100
  videos[i].content_loc = resolve(videos[i].content_loc, resolvers);
101
101
  }
102
102
  }
103
+ e.videos = mergeOnKey(videos, "content_loc");
103
104
  }
104
105
  return e;
105
106
  }
@@ -2,35 +2,70 @@ import { getRequestHost } from "h3";
2
2
  import { defu } from "defu";
3
3
  import { parseURL } from "ufo";
4
4
  import { extractSitemapXML } from "../utils/extractSitemapXML.js";
5
+ import { logger } from "../../../utils-pure.js";
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
+ }
5
31
  export async function fetchDataSource(input, event) {
6
32
  const context = typeof input.context === "string" ? { name: input.context } : input.context || { name: "fetch" };
7
- context.tips = context.tips || [];
8
33
  const url = typeof input.fetch === "string" ? input.fetch : input.fetch[0];
9
34
  const options = typeof input.fetch === "string" ? {} : input.fetch[1];
10
35
  const start = Date.now();
11
- const timeout = options.timeout || 5e3;
36
+ const isExternalUrl = !url.startsWith("/");
37
+ const timeout = isExternalUrl ? 1e4 : options.timeout || 5e3;
12
38
  const timeoutController = new AbortController();
13
39
  const abortRequestTimeout = setTimeout(() => timeoutController.abort(), timeout);
14
- let isMaybeErrorResponse = false;
15
- const isXmlRequest = parseURL(url).pathname.endsWith(".xml");
16
- const fetchContainer = url.startsWith("/") && event ? event : globalThis;
17
40
  try {
18
- const res = await fetchContainer.$fetch(url, {
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 = {
19
51
  ...options,
20
52
  responseType: isXmlRequest ? "text" : "json",
21
53
  signal: timeoutController.signal,
22
- headers: defu(options?.headers, {
23
- Accept: isXmlRequest ? "text/xml" : "application/json"
24
- }, event ? { host: getRequestHost(event, { xForwardedHost: true }) } : {}),
54
+ headers: mergedHeaders,
55
+ // Use ofetch's built-in retry for external sources
56
+ ...isExternalUrl && {
57
+ retry: 2,
58
+ retryDelay: 200
59
+ },
25
60
  // @ts-expect-error untyped
26
61
  onResponse({ response }) {
27
62
  if (typeof response._data === "string" && response._data.startsWith("<!DOCTYPE html>"))
28
63
  isMaybeErrorResponse = true;
29
64
  }
30
- });
65
+ };
66
+ const res = await tryFetchWithFallback(url, fetchOptions, event);
31
67
  const timeTakenMs = Date.now() - start;
32
68
  if (isMaybeErrorResponse) {
33
- context.tips.push("This is usually because the URL isn't correct or is throwing an error. Please check the URL");
34
69
  return {
35
70
  ...input,
36
71
  context,
@@ -53,16 +88,26 @@ export async function fetchDataSource(input, event) {
53
88
  };
54
89
  } catch (_err) {
55
90
  const error = _err;
56
- if (error.message.includes("This operation was aborted"))
57
- context.tips.push("The request has taken too long. Make sure app sources respond within 5 seconds or adjust the timeout fetch option.");
58
- else
59
- context.tips.push(`Response returned a status of ${error.response?.status || "unknown"}.`);
60
- console.error("[@nuxtjs/sitemap] Failed to fetch source.", { url, error });
91
+ if (isExternalUrl) {
92
+ const errorInfo = {
93
+ url,
94
+ timeout,
95
+ error: error.message,
96
+ statusCode: error.response?.status,
97
+ statusText: error.response?.statusText,
98
+ method: options?.method || "GET"
99
+ };
100
+ logger.error("Failed to fetch external source.", errorInfo);
101
+ } else {
102
+ logger.error("Failed to fetch source.", { url, error: error.message });
103
+ }
61
104
  return {
62
105
  ...input,
63
106
  context,
64
107
  urls: [],
65
- error: error.message
108
+ error: error.message,
109
+ _isFailure: true
110
+ // Mark as failure to prevent caching
66
111
  };
67
112
  } finally {
68
113
  if (abortRequestTimeout) {
@@ -1,4 +1,5 @@
1
1
  import type { H3Event } from 'h3';
2
2
  import type { ModuleRuntimeConfig } from '../types.js';
3
3
  export * from '../utils-pure.js';
4
+ export declare function xmlEscape(str: string): string;
4
5
  export declare function useSitemapRuntimeConfig(e?: H3Event): ModuleRuntimeConfig;
@@ -1,6 +1,9 @@
1
1
  import { useRuntimeConfig } from "nitropack/runtime";
2
2
  import { normalizeRuntimeFilters } from "../utils-pure.js";
3
3
  export * from "../utils-pure.js";
4
+ export function xmlEscape(str) {
5
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
6
+ }
4
7
  export function useSitemapRuntimeConfig(e) {
5
8
  const clone = JSON.parse(JSON.stringify(useRuntimeConfig(e).sitemap));
6
9
  for (const k in clone.sitemaps) {
@@ -178,6 +178,7 @@ export interface SitemapSourceResolved extends Omit<SitemapSourceBase, 'urls'> {
178
178
  urls: SitemapUrlInput[];
179
179
  error?: any;
180
180
  timeTakenMs?: number;
181
+ _isFailure?: boolean;
181
182
  }
182
183
  export type AppSourceContext = 'nuxt:pages' | 'nuxt:prerender' | 'nuxt:route-rules' | '@nuxtjs/i18n:pages' | '@nuxt/content:document-driven';
183
184
  export type SitemapSourceInput = string | [string, FetchOptions] | SitemapSourceBase | SitemapSourceResolved;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/sitemap",
3
3
  "type": "module",
4
- "version": "7.3.1",
4
+ "version": "7.4.0",
5
5
  "description": "Powerfully flexible XML Sitemaps that integrate seamlessly, for Nuxt.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -44,8 +44,8 @@
44
44
  }
45
45
  },
46
46
  "dependencies": {
47
- "@nuxt/devtools-kit": "^2.4.1",
48
- "@nuxt/kit": "^3.17.4",
47
+ "@nuxt/devtools-kit": "^2.5.0",
48
+ "@nuxt/kit": "^3.17.5",
49
49
  "chalk": "^5.4.1",
50
50
  "defu": "^6.1.4",
51
51
  "h3-compression": "^0.3.2",
@@ -59,23 +59,24 @@
59
59
  "ufo": "^1.6.1"
60
60
  },
61
61
  "devDependencies": {
62
- "@arethetypeswrong/cli": "^0.18.1",
63
- "@nuxt/content": "^3.5.1",
62
+ "@arethetypeswrong/cli": "^0.18.2",
63
+ "@nuxt/content": "^3.6.0",
64
64
  "@nuxt/eslint-config": "^1.4.1",
65
65
  "@nuxt/module-builder": "^1.0.1",
66
66
  "@nuxt/test-utils": "^3.19.1",
67
67
  "@nuxt/ui": "^3.1.3",
68
68
  "@nuxtjs/i18n": "^9.5.5",
69
69
  "@nuxtjs/robots": "^5.2.10",
70
+ "better-sqlite3": "^11.10.0",
70
71
  "bumpp": "^10.1.1",
71
- "eslint": "^9.28.0",
72
- "eslint-plugin-n": "^17.19.0",
72
+ "eslint": "^9.29.0",
73
+ "eslint-plugin-n": "^17.20.0",
73
74
  "execa": "^9.6.0",
74
- "happy-dom": "^17.6.1",
75
- "nuxt": "^3.17.4",
75
+ "happy-dom": "^18.0.1",
76
+ "nuxt": "^3.17.5",
76
77
  "nuxt-i18n-micro": "^1.87.0",
77
78
  "typescript": "^5.8.3",
78
- "vitest": "^3.2.0",
79
+ "vitest": "^3.2.3",
79
80
  "vue-tsc": "^2.2.10"
80
81
  },
81
82
  "scripts": {
@@ -1 +0,0 @@
1
- const e={};export{e as default};