@nuxtjs/sitemap 7.0.2 → 7.2.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 (40) hide show
  1. package/dist/client/200.html +8 -8
  2. package/dist/client/404.html +8 -8
  3. package/dist/client/_nuxt/{BLDCaNbg.js → B2vK47Ag.js} +1 -1
  4. package/dist/client/_nuxt/{B5lI3vl2.js → DIH-cllJ.js} +1 -1
  5. package/dist/client/_nuxt/{BCt9_I41.js → Djzw0wLt.js} +1 -1
  6. package/dist/client/_nuxt/builds/latest.json +1 -1
  7. package/dist/client/_nuxt/builds/meta/cbd506e7-b089-42a0-8062-dff717a0d923.json +1 -0
  8. package/dist/client/_nuxt/error-404.DWO74KKP.css +1 -0
  9. package/dist/client/_nuxt/error-500.DVC0lzO_.css +1 -0
  10. package/dist/client/_nuxt/sSZQ7D2b.js +32 -0
  11. package/dist/client/index.html +8 -8
  12. package/dist/module.d.mts +4 -1
  13. package/dist/module.d.ts +4 -1
  14. package/dist/module.json +1 -1
  15. package/dist/module.mjs +220 -72
  16. package/dist/runtime/server/plugins/{nuxt-content.js → nuxt-content-v2.js} +2 -10
  17. package/dist/runtime/server/routes/__sitemap__/debug.d.ts +1 -1
  18. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.d.ts +2 -0
  19. package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.js +17 -0
  20. package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +1 -1
  21. package/dist/runtime/server/routes/sitemap_index.xml.js +2 -2
  22. package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +2 -1
  23. package/dist/runtime/server/sitemap/builder/sitemap-index.js +8 -3
  24. package/dist/runtime/server/sitemap/builder/sitemap.d.ts +4 -3
  25. package/dist/runtime/server/sitemap/builder/sitemap.js +12 -7
  26. package/dist/runtime/server/sitemap/nitro.js +6 -2
  27. package/dist/runtime/server/sitemap/urlset/sources.js +16 -7
  28. package/dist/runtime/server/sitemap/utils/extractSitemapXML.d.ts +2 -0
  29. package/dist/runtime/server/sitemap/utils/extractSitemapXML.js +75 -0
  30. package/dist/runtime/types.d.ts +5 -1
  31. package/dist/types.d.mts +1 -1
  32. package/dist/types.d.ts +1 -1
  33. package/package.json +14 -13
  34. package/dist/client/_nuxt/BZ4NIl-o.js +0 -32
  35. package/dist/client/_nuxt/builds/meta/286d11d8-5fae-4d47-bddf-64b0ae9fc73d.json +0 -1
  36. package/dist/client/_nuxt/error-404.vQ0SaaqA.css +0 -1
  37. package/dist/client/_nuxt/error-500.ayRVCnRF.css +0 -1
  38. /package/dist/runtime/server/plugins/{nuxt-content.d.ts → nuxt-content-v2.d.ts} +0 -0
  39. /package/dist/runtime/server/routes/__sitemap__/{nuxt-content-urls.d.ts → nuxt-content-urls-v2.d.ts} +0 -0
  40. /package/dist/runtime/server/routes/__sitemap__/{nuxt-content-urls.js → nuxt-content-urls-v2.js} +0 -0
@@ -5,7 +5,7 @@ import { childSitemapSources, globalSitemapSources, resolveSitemapSources } from
5
5
  import { sortSitemapUrls } from "../urlset/sort.js";
6
6
  import { createPathFilter, logger, splitForLocales } from "../../../utils-pure.js";
7
7
  import { handleEntry, wrapSitemapXml } from "./xml.js";
8
- export function resolveSitemapEntries(sitemap, sources, runtimeConfig, resolvers) {
8
+ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers) {
9
9
  const {
10
10
  autoI18n,
11
11
  isI18nMapped
@@ -14,7 +14,7 @@ export function resolveSitemapEntries(sitemap, sources, runtimeConfig, resolvers
14
14
  include: sitemap.include,
15
15
  exclude: sitemap.exclude
16
16
  });
17
- const _urls = sources.flatMap((e) => e.urls).map((_e) => {
17
+ const _urls = urls.map((_e) => {
18
18
  const e = preNormalizeEntry(_e, resolvers);
19
19
  if (!e.loc || !filterPath(e.loc))
20
20
  return false;
@@ -139,7 +139,7 @@ export function resolveSitemapEntries(sitemap, sources, runtimeConfig, resolvers
139
139
  }
140
140
  return _urls;
141
141
  }
142
- export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig) {
142
+ export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig, nitro) {
143
143
  const {
144
144
  sitemaps,
145
145
  // enhancing
@@ -175,10 +175,15 @@ export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig) {
175
175
  });
176
176
  }
177
177
  }
178
- const sources = sitemap.includeAppSources ? await globalSitemapSources() : [];
179
- sources.push(...await childSitemapSources(sitemap));
180
- const resolvedSources = await resolveSitemapSources(sources, resolvers.event);
181
- const enhancedUrls = resolveSitemapEntries(sitemap, resolvedSources, { autoI18n, isI18nMapped }, resolvers);
178
+ const sourcesInput = sitemap.includeAppSources ? await globalSitemapSources() : [];
179
+ sourcesInput.push(...await childSitemapSources(sitemap));
180
+ const sources = await resolveSitemapSources(sourcesInput, resolvers.event);
181
+ const resolvedCtx = {
182
+ urls: sources.flatMap((s) => s.urls),
183
+ sitemapName: sitemap.sitemapName
184
+ };
185
+ await nitro?.hooks.callHook("sitemap:input", resolvedCtx);
186
+ const enhancedUrls = resolveSitemapEntries(sitemap, resolvedCtx.urls, { autoI18n, isI18nMapped }, resolvers);
182
187
  const filteredUrls = enhancedUrls.filter((e) => {
183
188
  if (isMultiSitemap && e._sitemap && sitemap.sitemapName)
184
189
  return e._sitemap === sitemap.sitemapName;
@@ -4,7 +4,7 @@ import { defu } from "defu";
4
4
  import { logger, mergeOnKey, splitForLocales } from "../../utils-pure.js";
5
5
  import { createNitroRouteRuleMatcher } from "../kit.js";
6
6
  import { buildSitemapUrls, urlsToXml } from "./builder/sitemap.js";
7
- import { normaliseEntry } from "./urlset/normalise.js";
7
+ import { normaliseEntry, preNormalizeEntry } from "./urlset/normalise.js";
8
8
  import { sortSitemapUrls } from "./urlset/sort.js";
9
9
  import { useNitroApp, createSitePathResolver, getPathRobotConfig, useSiteConfig } from "#imports";
10
10
  export function useNitroUrlResolvers(e) {
@@ -39,7 +39,7 @@ export async function createSitemap(event, definition, runtimeConfig) {
39
39
  }
40
40
  }
41
41
  const resolvers = useNitroUrlResolvers(event);
42
- let sitemapUrls = await buildSitemapUrls(definition, resolvers, runtimeConfig);
42
+ let sitemapUrls = await buildSitemapUrls(definition, resolvers, runtimeConfig, nitro);
43
43
  const routeRuleMatcher = createNitroRouteRuleMatcher();
44
44
  const { autoI18n } = runtimeConfig;
45
45
  sitemapUrls = sitemapUrls.map((u) => {
@@ -63,11 +63,15 @@ export async function createSitemap(event, definition, runtimeConfig) {
63
63
  return false;
64
64
  return routeRules.sitemap ? defu(u, routeRules.sitemap) : u;
65
65
  }).filter(Boolean);
66
+ const locSize = sitemapUrls.length;
66
67
  const resolvedCtx = {
67
68
  urls: sitemapUrls,
68
69
  sitemapName
69
70
  };
70
71
  await nitro.hooks.callHook("sitemap:resolved", resolvedCtx);
72
+ if (resolvedCtx.urls.length !== locSize) {
73
+ resolvedCtx.urls = resolvedCtx.urls.map((e) => preNormalizeEntry(e, resolvers));
74
+ }
71
75
  const maybeSort = (urls2) => runtimeConfig.sortEntries ? sortSitemapUrls(urls2) : urls2;
72
76
  const normalizedPreDedupe = resolvedCtx.urls.map((e) => normaliseEntry(e, definition.defaults, resolvers));
73
77
  const urls = maybeSort(mergeOnKey(normalizedPreDedupe, "_key").map((e) => normaliseEntry(e, definition.defaults, resolvers)));
@@ -1,5 +1,7 @@
1
1
  import { getRequestHost } from "h3";
2
2
  import { defu } from "defu";
3
+ import { parseURL } from "ufo";
4
+ import { extractSitemapXML } from "../utils/extractSitemapXML.js";
3
5
  export async function fetchDataSource(input, event) {
4
6
  const context = typeof input.context === "string" ? { name: input.context } : input.context || { name: "fetch" };
5
7
  context.tips = context.tips || [];
@@ -9,24 +11,25 @@ export async function fetchDataSource(input, event) {
9
11
  const timeout = options.timeout || 5e3;
10
12
  const timeoutController = new AbortController();
11
13
  const abortRequestTimeout = setTimeout(() => timeoutController.abort(), timeout);
12
- let isHtmlResponse = false;
14
+ let isMaybeErrorResponse = false;
15
+ const isXmlRequest = parseURL(url).pathname.endsWith(".xml");
16
+ const fetchContainer = url.startsWith("/") && event ? event : globalThis;
13
17
  try {
14
- const fetchContainer = url.startsWith("/") && event ? event : globalThis;
15
- const urls = await fetchContainer.$fetch(url, {
18
+ const res = await fetchContainer.$fetch(url, {
16
19
  ...options,
17
- responseType: "json",
20
+ responseType: isXmlRequest ? "text" : "json",
18
21
  signal: timeoutController.signal,
19
22
  headers: defu(options?.headers, {
20
- Accept: "application/json"
23
+ Accept: isXmlRequest ? "text/xml" : "application/json"
21
24
  }, event ? { Host: getRequestHost(event, { xForwardedHost: true }) } : {}),
22
25
  // @ts-expect-error untyped
23
26
  onResponse({ response }) {
24
27
  if (typeof response._data === "string" && response._data.startsWith("<!DOCTYPE html>"))
25
- isHtmlResponse = true;
28
+ isMaybeErrorResponse = true;
26
29
  }
27
30
  });
28
31
  const timeTakenMs = Date.now() - start;
29
- if (isHtmlResponse) {
32
+ if (isMaybeErrorResponse) {
30
33
  context.tips.push("This is usually because the URL isn't correct or is throwing an error. Please check the URL");
31
34
  return {
32
35
  ...input,
@@ -36,6 +39,12 @@ export async function fetchDataSource(input, event) {
36
39
  error: "Received HTML response instead of JSON"
37
40
  };
38
41
  }
42
+ let urls = [];
43
+ if (typeof res === "object") {
44
+ urls = res.urls || res;
45
+ } else if (typeof res === "string" && parseURL(url).pathname.endsWith(".xml")) {
46
+ urls = extractSitemapXML(res);
47
+ }
39
48
  return {
40
49
  ...input,
41
50
  context,
@@ -0,0 +1,2 @@
1
+ import type { SitemapUrlInput } from '../../../types.js';
2
+ export declare function extractSitemapXML(xml: string): SitemapUrlInput[];
@@ -0,0 +1,75 @@
1
+ export function extractSitemapXML(xml) {
2
+ const urls = xml.match(/<url>[\s\S]*?<\/url>/g) || [];
3
+ return urls.map((url) => {
4
+ const loc = url.match(/<loc>([^<]+)<\/loc>/)?.[1];
5
+ if (!loc) return null;
6
+ const lastmod = url.match(/<lastmod>([^<]+)<\/lastmod>/)?.[1];
7
+ const changefreq = url.match(/<changefreq>([^<]+)<\/changefreq>/)?.[1];
8
+ const priority = url.match(/<priority>([^<]+)<\/priority>/) ? Number.parseFloat(url.match(/<priority>([^<]+)<\/priority>/)[1]) : void 0;
9
+ const images = (url.match(/<image:image>[\s\S]*?<\/image:image>/g) || []).map((image) => {
10
+ const imageLoc = image.match(/<image:loc>([^<]+)<\/image:loc>/)?.[1];
11
+ return imageLoc ? { loc: imageLoc } : null;
12
+ }).filter(Boolean);
13
+ const videos = (url.match(/<video:video>[\s\S]*?<\/video:video>/g) || []).map((video) => {
14
+ const videoObj = {};
15
+ const title = video.match(/<video:title>([^<]+)<\/video:title>/)?.[1];
16
+ const thumbnail_loc = video.match(/<video:thumbnail_loc>([^<]+)<\/video:thumbnail_loc>/)?.[1];
17
+ const description = video.match(/<video:description>([^<]+)<\/video:description>/)?.[1];
18
+ const content_loc = video.match(/<video:content_loc>([^<]+)<\/video:content_loc>/)?.[1];
19
+ if (!title || !thumbnail_loc || !description || !content_loc) return null;
20
+ videoObj.title = title;
21
+ videoObj.thumbnail_loc = thumbnail_loc;
22
+ videoObj.description = description;
23
+ videoObj.content_loc = content_loc;
24
+ const player_loc = video.match(/<video:player_loc>([^<]+)<\/video:player_loc>/)?.[1];
25
+ if (player_loc) videoObj.player_loc = player_loc;
26
+ const duration = video.match(/<video:duration>([^<]+)<\/video:duration>/) ? Number.parseInt(video.match(/<video:duration>([^<]+)<\/video:duration>/)[1], 10) : void 0;
27
+ if (duration) videoObj.duration = duration;
28
+ const expiration_date = video.match(/<video:expiration_date>([^<]+)<\/video:expiration_date>/)?.[1];
29
+ if (expiration_date) videoObj.expiration_date = expiration_date;
30
+ const rating = video.match(/<video:rating>([^<]+)<\/video:rating>/) ? Number.parseFloat(video.match(/<video:rating>([^<]+)<\/video:rating>/)[1]) : void 0;
31
+ if (rating) videoObj.rating = rating;
32
+ const view_count = video.match(/<video:view_count>([^<]+)<\/video:view_count>/) ? Number.parseInt(video.match(/<video:view_count>([^<]+)<\/video:view_count>/)[1], 10) : void 0;
33
+ if (view_count) videoObj.view_count = view_count;
34
+ const publication_date = video.match(/<video:publication_date>([^<]+)<\/video:publication_date>/)?.[1];
35
+ if (publication_date) videoObj.publication_date = publication_date;
36
+ const family_friendly = video.match(/<video:family_friendly>([^<]+)<\/video:family_friendly>/)?.[1];
37
+ if (family_friendly) videoObj.family_friendly = family_friendly;
38
+ const restriction = video.match(/<video:restriction relationship="([^"]+)">([^<]+)<\/video:restriction>/);
39
+ if (restriction) videoObj.restriction = { relationship: restriction[1], restriction: restriction[2] };
40
+ const platform = video.match(/<video:platform relationship="([^"]+)">([^<]+)<\/video:platform>/);
41
+ if (platform) videoObj.platform = { relationship: platform[1], platform: platform[2] };
42
+ const price = (video.match(/<video:price [^>]+>([^<]+)<\/video:price>/g) || []).map((price2) => {
43
+ const priceValue = price2.match(/<video:price [^>]+>([^<]+)<\/video:price>/)?.[1];
44
+ const currency = price2.match(/currency="([^"]+)"/)?.[1];
45
+ const type = price2.match(/type="([^"]+)"/)?.[1];
46
+ return priceValue ? { price: priceValue, currency, type } : null;
47
+ }).filter(Boolean);
48
+ if (price.length) videoObj.price = price;
49
+ const requires_subscription = video.match(/<video:requires_subscription>([^<]+)<\/video:requires_subscription>/)?.[1];
50
+ if (requires_subscription) videoObj.requires_subscription = requires_subscription;
51
+ const uploader = video.match(/<video:uploader info="([^"]+)">([^<]+)<\/video:uploader>/);
52
+ if (uploader) videoObj.uploader = { uploader: uploader[2], info: uploader[1] };
53
+ const live = video.match(/<video:live>([^<]+)<\/video:live>/)?.[1];
54
+ if (live) videoObj.live = live;
55
+ const tag = (video.match(/<video:tag>([^<]+)<\/video:tag>/g) || []).map((tag2) => tag2.match(/<video:tag>([^<]+)<\/video:tag>/)?.[1]).filter(Boolean);
56
+ if (tag.length) videoObj.tag = tag;
57
+ return videoObj;
58
+ }).filter(Boolean);
59
+ const alternatives = (url.match(/<xhtml:link[\s\S]*?\/>/g) || []).map((link) => {
60
+ const hreflang = link.match(/hreflang="([^"]+)"/)?.[1];
61
+ const href = link.match(/href="([^"]+)"/)?.[1];
62
+ return hreflang && href ? { hreflang, href } : null;
63
+ }).filter(Boolean);
64
+ const news = url.match(/<news:news>[\s\S]*?<\/news:news>/) ? {
65
+ title: url.match(/<news:title>([^<]+)<\/news:title>/)?.[1],
66
+ publication_date: url.match(/<news:publication_date>([^<]+)<\/news:publication_date>/)?.[1],
67
+ publication: {
68
+ name: url.match(/<news:name>([^<]+)<\/news:name>/)?.[1],
69
+ language: url.match(/<news:language>([^<]+)<\/news:language>/)?.[1]
70
+ }
71
+ } : void 0;
72
+ const urlObj = { loc, lastmod, changefreq, priority, images, videos, alternatives, news };
73
+ return Object.fromEntries(Object.entries(urlObj).filter(([_, v]) => v != null && v.length !== 0));
74
+ }).filter(Boolean);
75
+ }
@@ -45,7 +45,7 @@ export interface ModuleOptions extends SitemapDefinition {
45
45
  *
46
46
  * @default /__sitemap__/
47
47
  */
48
- sitemapsPathPrefix: string;
48
+ sitemapsPathPrefix: string | false;
49
49
  /**
50
50
  * Sitemaps to append to the sitemap index.
51
51
  *
@@ -303,6 +303,10 @@ export interface SitemapRenderCtx {
303
303
  sitemapName: string;
304
304
  urls: ResolvedSitemapUrl[];
305
305
  }
306
+ export interface SitemapInputCtx {
307
+ sitemapName: string;
308
+ urls: SitemapUrlInput[];
309
+ }
306
310
  export interface SitemapOutputHookCtx {
307
311
  sitemapName: string;
308
312
  sitemap: string;
package/dist/types.d.mts CHANGED
@@ -1 +1 @@
1
- export { type ModuleOptions, default } from './module.js'
1
+ export { type ModuleOptions, type asSitemapCollection, default } from './module.js'
package/dist/types.d.ts CHANGED
@@ -1 +1 @@
1
- export { type ModuleOptions, default } from './module'
1
+ export { type ModuleOptions, type asSitemapCollection, default } from './module'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/sitemap",
3
3
  "type": "module",
4
- "version": "7.0.2",
4
+ "version": "7.2.0",
5
5
  "description": "Powerfully flexible XML Sitemaps that integrate seamlessly, for Nuxt.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -36,35 +36,35 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@nuxt/devtools-kit": "^1.7.0",
39
- "@nuxt/kit": "^3.15.1",
39
+ "@nuxt/kit": "^3.15.2",
40
40
  "chalk": "^5.4.1",
41
41
  "defu": "^6.1.4",
42
42
  "h3-compression": "^0.3.2",
43
43
  "nuxt-site-config": "^3.0.6",
44
44
  "ofetch": "^1.4.1",
45
- "pathe": "^2.0.1",
46
- "pkg-types": "^1.3.0",
45
+ "pathe": "^2.0.2",
46
+ "pkg-types": "^1.3.1",
47
47
  "radix3": "^1.1.2",
48
48
  "semver": "^7.6.3",
49
49
  "sirv": "^3.0.0",
50
50
  "ufo": "^1.5.4"
51
51
  },
52
52
  "devDependencies": {
53
- "@nuxt/content": "^2.13.4",
53
+ "@nuxt/content": "^3.0.0",
54
54
  "@nuxt/eslint-config": "^0.7.5",
55
55
  "@nuxt/module-builder": "0.8.4",
56
56
  "@nuxt/test-utils": "^3.15.4",
57
- "@nuxt/ui": "^2.20.0",
57
+ "@nuxt/ui": "^2.21.0",
58
58
  "@nuxtjs/i18n": "9.1.1",
59
- "@nuxtjs/robots": "5.1.0",
60
- "bumpp": "^9.10.0",
61
- "eslint": "9.17.0",
59
+ "@nuxtjs/robots": "^5.1.1",
60
+ "bumpp": "^9.10.1",
61
+ "eslint": "^9.18.0",
62
62
  "eslint-plugin-n": "^17.15.1",
63
63
  "execa": "^9.5.2",
64
- "nuxt": "^3.15.1",
65
- "nuxt-i18n-micro": "^1.59.0",
64
+ "nuxt": "^3.15.2",
65
+ "nuxt-i18n-micro": "^1.64.0",
66
66
  "typescript": "5.6.3",
67
- "vitest": "^2.1.8"
67
+ "vitest": "^3.0.2"
68
68
  },
69
69
  "resolutions": {
70
70
  "postcss": "8.4.47",
@@ -75,7 +75,8 @@
75
75
  "h3",
76
76
  "std-env",
77
77
  "nitropack",
78
- "consola"
78
+ "consola",
79
+ "@nuxt/content"
79
80
  ]
80
81
  },
81
82
  "scripts": {