@nuxtjs/sitemap 8.3.3 → 8.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.
@@ -1,7 +1,10 @@
1
+ import { fileURLToPath } from 'node:url'
1
2
  import { resolve } from 'pathe'
2
3
 
4
+ const currentDir = fileURLToPath(new URL('.', import.meta.url))
5
+
3
6
  // Nuxt SEO devtools panel, shipped as a layer (Model C). Components flat-registered
4
7
  // so intra-panel references resolve by name.
5
8
  export default defineNuxtConfig({
6
- components: [{ path: resolve(__dirname, './components'), pathPrefix: false }],
9
+ components: [{ path: resolve(currentDir, './components'), pathPrefix: false }],
7
10
  })
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "nuxt": ">=3.9.0"
5
5
  },
6
6
  "configKey": "sitemap",
7
- "version": "8.3.3",
7
+ "version": "8.4.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -2,22 +2,25 @@ import { useNuxt, hasNuxtModule, addTypeTemplate, addTemplate, defineNuxtModule,
2
2
  import { defu } from 'defu';
3
3
  import { withSiteUrl, installNuxtSiteConfig } from 'nuxt-site-config/kit';
4
4
  import { isPathFile } from 'nuxt-site-config/urls';
5
- import { isNuxtGenerate, renderNitroTypeAugmentations, useModuleLogger, setupNitroRuntimeCompatibility, getNuxtModuleOptions, resolveNitroPreset, resolveNuxtContentVersion, createPagesPromise, createNitroPromise } from 'nuxtseo-shared/kit';
5
+ import { isNuxtGenerate, renderNitroTypeAugmentations, useModuleLogger, setupNitroRuntimeCompatibility, getNuxtModuleOptions, resolveNitroPreset, resolveContentProvider, setupContentRuntime, createPagesPromise, createNitroPromise } from 'nuxtseo-shared/kit';
6
6
  import { serializeFilters } from 'nuxtseo-shared/utils';
7
7
  import { dirname, extname } from 'pathe';
8
8
  import { readPackageJSON } from 'pkg-types';
9
- import { withBase, withHttps, withTrailingSlash, withoutLeadingSlash, joinURL, withLeadingSlash, withoutTrailingSlash } from 'ufo';
9
+ import { withBase, withHttps, withTrailingSlash, withoutLeadingSlash, joinURL, withLeadingSlash } from 'ufo';
10
10
  import { setupDevToolsUI as setupDevToolsUI$1 } from 'nuxtseo-shared/devtools';
11
11
  import { readFileSync, statSync } from 'node:fs';
12
- import { mkdir, writeFile } from 'node:fs/promises';
12
+ import { rm, mkdir, writeFile } from 'node:fs/promises';
13
13
  import { join } from 'node:path';
14
14
  import { colors } from 'consola/utils';
15
15
  import { splitForLocales, createPathFilter } from '../dist/runtime/utils-pure.js';
16
16
  import { p as parseHtmlExtractSitemapMeta } from './shared/sitemap.BoMnWHOt.mjs';
17
17
  import { normaliseDate } from '../dist/runtime/server/sitemap/urlset/normalise.js';
18
- import { splitPathForI18nLocales as splitPathForI18nLocales$1, expandCompactLocaleRoute, normalizeLocales, generatePathForI18nPages } from 'nuxtseo-shared/i18n';
18
+ import { mapPathForI18nPages, splitPathForI18nLocales as splitPathForI18nLocales$1, expandCompactLocaleRoute, normalizeLocales, generatePathForI18nPages } from 'nuxtseo-shared/i18n';
19
19
  import 'ultrahtml';
20
20
 
21
+ const COMARK_CONTENT_SOURCE = "@harlan-zw/comark-content:urls";
22
+ const COMARK_CONTENT_SITEMAP_ROUTE = "/__sitemap__/comark-content-urls.json";
23
+
21
24
  function setupDevToolsUI(_options, resolve, nuxt = useNuxt()) {
22
25
  setupDevToolsUI$1(
23
26
  { route: "/__nuxt-sitemap", name: "sitemap", title: "Sitemap", icon: "carbon:load-balancer-application" },
@@ -72,6 +75,10 @@ export async function readSourcesFromFilesystem(filename) {
72
75
  `;
73
76
  });
74
77
  nuxt.hooks.hook("nitro:init", async (nitro) => {
78
+ await Promise.all([
79
+ rm(join(runtimeAssetsPath, "global-sources.json"), { force: true }),
80
+ rm(join(runtimeAssetsPath, "child-sources.json"), { force: true })
81
+ ]);
75
82
  nitro.hooks.hook("prerender:generate", async (route) => {
76
83
  const html = route.contents;
77
84
  if (!route.fileName?.endsWith(".html") || !html || ["/200.html", "/404.html"].includes(route.route))
@@ -260,6 +267,33 @@ function splitPathForI18nLocales(path, autoI18n) {
260
267
  return path;
261
268
  return splitPathForI18nLocales$1(path, autoI18n);
262
269
  }
270
+ function uniquePaths(paths) {
271
+ return [...new Set(paths)];
272
+ }
273
+ function resolveI18nFilterPaths(path, autoI18n) {
274
+ if (typeof path !== "string")
275
+ return [path];
276
+ const mappedPaths = mapPathForI18nPages(path, autoI18n);
277
+ if (mappedPaths === false) {
278
+ if (autoI18n.strategy === "no_prefix")
279
+ return [path];
280
+ const splitPaths = splitPathForI18nLocales$1(path, autoI18n);
281
+ return Array.isArray(splitPaths) ? splitPaths : [splitPaths];
282
+ }
283
+ if (autoI18n.strategy === "prefix" || autoI18n.strategy === "no_prefix")
284
+ return uniquePaths(mappedPaths);
285
+ const defaultLocales = autoI18n.locales.filter((locale) => locale.code === autoI18n.defaultLocale);
286
+ const defaultStrategy = autoI18n.strategy === "prefix_and_default" ? "prefix" : "no_prefix";
287
+ const defaultPaths = mapPathForI18nPages(path, {
288
+ ...autoI18n,
289
+ locales: defaultLocales,
290
+ strategy: defaultStrategy
291
+ });
292
+ return uniquePaths([
293
+ ...defaultPaths || [path],
294
+ ...mappedPaths
295
+ ]);
296
+ }
263
297
 
264
298
  async function resolveUrls(urls, ctx) {
265
299
  try {
@@ -428,7 +462,34 @@ function generateExtraRoutesFromNuxtConfig(nuxt = useNuxt()) {
428
462
  }).map(([k]) => k).filter(filterForValidPage);
429
463
  return { routeRules };
430
464
  }
465
+ function resolveExcludedAppSources(resolved, authored) {
466
+ if (resolved === true || authored === true)
467
+ return true;
468
+ if (!Array.isArray(authored))
469
+ return resolved;
470
+ const excluded = [...resolved];
471
+ for (const source of authored) {
472
+ if (typeof source === "string" && !excluded.includes(source))
473
+ excluded.push(source);
474
+ }
475
+ return excluded;
476
+ }
431
477
 
478
+ const IMAGE_TAGS = /* @__PURE__ */ new Set(["image", "img", "nuxtimg", "nuxt-img"]);
479
+ function discoverContentImages(body) {
480
+ const images = [];
481
+ const walk = (nodes) => {
482
+ for (const node of nodes || []) {
483
+ if (!Array.isArray(node) || typeof node[0] !== "string")
484
+ continue;
485
+ if (IMAGE_TAGS.has(node[0]) && node[1]?.src)
486
+ images.push({ loc: node[1].src });
487
+ walk(node.slice(2));
488
+ }
489
+ };
490
+ walk(body?.value ?? body?.nodes);
491
+ return images;
492
+ }
432
493
  const module$1 = defineNuxtModule({
433
494
  meta: {
434
495
  name: "@nuxtjs/sitemap",
@@ -453,6 +514,10 @@ const module$1 = defineNuxtModule({
453
514
  version: ">=2",
454
515
  optional: true
455
516
  },
517
+ "@harlan-zw/comark-content": {
518
+ version: ">=0.1.2",
519
+ optional: true
520
+ },
456
521
  "@nuxtjs/robots": {
457
522
  version: ">=4",
458
523
  optional: true
@@ -646,11 +711,11 @@ const module$1 = defineNuxtModule({
646
711
  const hasCustomI18nSitemaps = i18nSitemaps.length > 0;
647
712
  if (hasCustomI18nSitemaps) {
648
713
  for (const { name: name2, cfg } of i18nSitemaps) {
714
+ const { sitemapName: _sitemapName, _route, _isChunking, _chunkSize, _chunkCount, ...inheritedConfig } = cfg;
649
715
  for (const locale of resolvedAutoI18n.locales) {
650
716
  newSitemaps[`${locale._sitemap}-${name2}`] = {
651
- includeAppSources: true,
652
- ...cfg.exclude?.length && { exclude: cfg.exclude },
653
- ...cfg.include?.length && { include: cfg.include }
717
+ ...inheritedConfig,
718
+ includeAppSources: true
654
719
  };
655
720
  }
656
721
  }
@@ -743,20 +808,13 @@ const module$1 = defineNuxtModule({
743
808
  addServerPlugin(resolve("./runtime/server/plugins/stream-transport"));
744
809
  }
745
810
  const isNuxtContentDocumentDriven = !!nuxt.options.content?.documentDriven || config.strictNuxtContentPaths;
746
- const contentVersion = await resolveNuxtContentVersion();
747
- const isNuxtContentV3 = contentVersion && contentVersion.version === 3;
811
+ const contentProvider = await resolveContentProvider(nuxt);
812
+ const isNuxtContentV3 = contentProvider._tag === "NuxtContent" && contentProvider.version === 3;
813
+ const isNuxtContentV2 = contentProvider._tag === "NuxtContent" && contentProvider.version === 2;
814
+ const isComarkContent = contentProvider._tag === "Comark";
748
815
  const nuxtV3Collections = /* @__PURE__ */ new Set();
749
- const isNuxtContentV2 = contentVersion && contentVersion.version === 2;
750
- if (isNuxtContentV3) {
751
- if (nuxt.options._installedModules.some((m) => m.meta.name === "Content")) {
752
- logger.warn("You have loaded `@nuxt/content` before `@nuxtjs/sitemap`, this may cause issues with the integration. Please ensure `@nuxtjs/sitemap` is loaded first.");
753
- }
754
- config.exclude.push("/__nuxt_content/**");
755
- const needsCustomAlias = await hasNuxtModuleCompatibility("@nuxt/content", "<3.6.0");
756
- if (needsCustomAlias) {
757
- nuxt.options.alias["#sitemap/content-v3-nitro-path"] = resolve(dirname(resolveModule("@nuxt/content")), "runtime/nitro");
758
- nuxt.options.alias["@nuxt/content/nitro"] = resolve("./runtime/server/content-compat");
759
- }
816
+ setupContentRuntime(contentProvider, nuxt);
817
+ const registerContentSitemapHook = (options) => {
760
818
  nuxt.hooks.hook("content:file:afterParse", (ctx) => {
761
819
  try {
762
820
  const content = ctx.content;
@@ -765,7 +823,7 @@ const module$1 = defineNuxtModule({
765
823
  ctx.content.sitemap = null;
766
824
  return;
767
825
  }
768
- if (!ctx.collection.fields || !("sitemap" in ctx.collection.fields)) {
826
+ if (options.requireCollectionField && (!ctx.collection.fields || !("sitemap" in ctx.collection.fields))) {
769
827
  ctx.content.sitemap = null;
770
828
  return;
771
829
  }
@@ -778,13 +836,8 @@ const module$1 = defineNuxtModule({
778
836
  return;
779
837
  }
780
838
  const images = [];
781
- if (config.discoverImages) {
782
- images.push(
783
- ...content.body?.value?.filter(
784
- (c) => ["image", "img", "nuxtimg", "nuxt-img"].includes(c[0])
785
- ).filter((c) => c[1]?.src).map((c) => ({ loc: c[1].src })) || []
786
- );
787
- }
839
+ if (config.discoverImages)
840
+ images.push(...discoverContentImages(content.body));
788
841
  const lastmod = content.seo?.articleModifiedTime || content.updatedAt;
789
842
  const defaults = {
790
843
  loc: content.path
@@ -798,6 +851,8 @@ const module$1 = defineNuxtModule({
798
851
  logger.warn(`Failed to process sitemap data for content file (collection: ${ctx.collection?.name}, path: ${ctx.content?.path}), skipping.`, e);
799
852
  }
800
853
  });
854
+ };
855
+ const addContentCallbackVirtuals = () => {
801
856
  nuxt.hook("nitro:config", (nitroConfig) => {
802
857
  const filterEntries = [];
803
858
  if (globalThis.__sitemapCollectionFilters) {
@@ -815,9 +870,22 @@ ${filterEntries.join("\n")}`;
815
870
  nitroConfig.virtual["#sitemap/content-on-url"] = `export const onUrlFns = new Map()
816
871
  ${onUrlEntries.join("\n")}`;
817
872
  });
873
+ };
874
+ if (isNuxtContentV3) {
875
+ if (nuxt.options._installedModules.some((m) => m.meta.name === "Content")) {
876
+ logger.warn("You have loaded `@nuxt/content` before `@nuxtjs/sitemap`, this may cause issues with the integration. Please ensure `@nuxtjs/sitemap` is loaded first.");
877
+ }
878
+ config.exclude.push("/__nuxt_content/**");
879
+ const needsCustomAlias = await hasNuxtModuleCompatibility("@nuxt/content", "<3.6.0");
880
+ if (needsCustomAlias) {
881
+ nuxt.options.alias["#sitemap/content-v3-nitro-path"] = resolve(dirname(resolveModule("@nuxt/content")), "runtime/nitro");
882
+ nuxt.options.alias["@nuxt/content/nitro"] = resolve("./runtime/server/content-compat");
883
+ }
884
+ registerContentSitemapHook({ requireCollectionField: true });
885
+ addContentCallbackVirtuals();
818
886
  addServerHandler({
819
887
  route: "/__sitemap__/nuxt-content-urls.json",
820
- handler: resolve("./runtime/server/routes/__sitemap__/nuxt-content-urls-v3")
888
+ handler: resolve("./runtime/server/routes/__sitemap__/content-urls")
821
889
  });
822
890
  if (config.strictNuxtContentPaths) {
823
891
  logger.warn("You have set `strictNuxtContentPaths: true` but are using @nuxt/content v3. This is not required, please remove it.");
@@ -830,6 +898,24 @@ ${onUrlEntries.join("\n")}`;
830
898
  },
831
899
  fetch: "/__sitemap__/nuxt-content-urls.json"
832
900
  });
901
+ } else if (isComarkContent) {
902
+ registerContentSitemapHook({ requireCollectionField: false });
903
+ addContentCallbackVirtuals();
904
+ addServerHandler({
905
+ route: COMARK_CONTENT_SITEMAP_ROUTE,
906
+ handler: resolve("./runtime/server/routes/__sitemap__/content-urls")
907
+ });
908
+ if (config.strictNuxtContentPaths) {
909
+ logger.warn("You have set `strictNuxtContentPaths: true` but are using comark-content. This is not required, please remove it.");
910
+ }
911
+ appGlobalSources.push({
912
+ context: {
913
+ name: COMARK_CONTENT_SOURCE,
914
+ description: "Generated from your markdown files.",
915
+ tips: nuxtV3Collections.size ? [`Parsing the following collections: ${Array.from(nuxtV3Collections).join(", ")}`] : ["No collections found. Set `sitemap: false` on a collection to keep it out."]
916
+ },
917
+ fetch: COMARK_CONTENT_SITEMAP_ROUTE
918
+ });
833
919
  } else if (isNuxtContentV2) {
834
920
  addServerPlugin(resolve("./runtime/server/plugins/nuxt-content-v2"));
835
921
  addServerHandler({
@@ -966,38 +1052,15 @@ ${onUrlEntries.join("\n")}`;
966
1052
  };
967
1053
  }
968
1054
  if (resolvedAutoI18n && usingI18nPages && !hasDisabledAutoI18n) {
969
- const pages = nuxtI18nConfig?.pages || {};
1055
+ const i18n = resolvedAutoI18n;
970
1056
  for (const sitemapName in sitemaps) {
971
- let mapToI18nPages = function(path) {
972
- if (typeof path !== "string")
973
- return [path];
974
- const withoutSlashes = withoutTrailingSlash(withoutLeadingSlash(path)).replace("/index", "");
975
- if (pages && withoutSlashes in pages) {
976
- const pageLocales = pages[withoutSlashes];
977
- if (pageLocales) {
978
- return Object.keys(pageLocales).map((localeCode) => withLeadingSlash(generatePathForI18nPages({
979
- localeCode,
980
- pageLocales: pageLocales[localeCode],
981
- nuxtI18nConfig,
982
- normalisedLocales
983
- })));
984
- }
985
- }
986
- let match = [path];
987
- Object.values(pages).forEach((pageLocales) => {
988
- if (pageLocales && nuxtI18nConfig.defaultLocale in pageLocales && pageLocales[nuxtI18nConfig.defaultLocale] === path)
989
- match = Object.keys(pageLocales).map((localeCode) => withLeadingSlash(generatePathForI18nPages({ localeCode, pageLocales: pageLocales[localeCode], nuxtI18nConfig, normalisedLocales })));
990
- });
991
- return match;
992
- };
993
1057
  if (["index", "chunks"].includes(sitemapName))
994
1058
  continue;
995
1059
  const sitemap = sitemaps[sitemapName];
996
- sitemap.include = (sitemap.include || []).flatMap((path) => mapToI18nPages(path));
997
- sitemap.exclude = (sitemap.exclude || []).flatMap((path) => mapToI18nPages(path));
1060
+ sitemap.include = (sitemap.include || []).flatMap((path) => resolveI18nFilterPaths(path, i18n));
1061
+ sitemap.exclude = (sitemap.exclude || []).flatMap((path) => resolveI18nFilterPaths(path, i18n));
998
1062
  }
999
- }
1000
- if (resolvedAutoI18n && resolvedAutoI18n.locales && resolvedAutoI18n.strategy !== "no_prefix") {
1063
+ } else if (resolvedAutoI18n && resolvedAutoI18n.locales && resolvedAutoI18n.strategy !== "no_prefix") {
1001
1064
  const i18n = resolvedAutoI18n;
1002
1065
  for (const sitemapName in sitemaps) {
1003
1066
  if (["index", "chunks"].includes(sitemapName))
@@ -1093,6 +1156,10 @@ ${onUrlEntries.join("\n")}`;
1093
1156
  return r.contentType?.includes("text/html");
1094
1157
  };
1095
1158
  const generateGlobalSources = async () => {
1159
+ const excludedAppSources = resolveExcludedAppSources(
1160
+ config.excludeAppSources,
1161
+ nuxt.options.sitemap?.excludeAppSources
1162
+ );
1096
1163
  const { routeRules: routeRules2 } = generateExtraRoutesFromNuxtConfig();
1097
1164
  const nitro = await nitroPromise;
1098
1165
  const prerenderedRoutes2 = nitro._prerenderedRoutes || [];
@@ -1169,7 +1236,7 @@ ${onUrlEntries.join("\n")}`;
1169
1236
  s.sourceType = "user";
1170
1237
  return s;
1171
1238
  }),
1172
- ...(config.excludeAppSources === true ? [] : [
1239
+ ...(excludedAppSources === true ? [] : [
1173
1240
  ...appGlobalSources,
1174
1241
  {
1175
1242
  context: {
@@ -1201,7 +1268,7 @@ ${onUrlEntries.join("\n")}`;
1201
1268
  },
1202
1269
  urls: prerenderUrlsFinal
1203
1270
  }
1204
- ]).filter((s) => !config.excludeAppSources.includes(s.context.name) && (!!s.urls?.length || !!s.fetch)).map((s) => {
1271
+ ]).filter((s) => !excludedAppSources.includes(s.context.name) && (!!s.urls?.length || !!s.fetch)).map((s) => {
1205
1272
  s.sourceType = "app";
1206
1273
  return s;
1207
1274
  })
@@ -0,0 +1,9 @@
1
+ /**
2
+ * URLs from the installed content module's page collections.
3
+ *
4
+ * One route for every provider. `#nuxtseo/content` normalizes the manifest shape
5
+ * and the query builder; the `sitemap` field on each entry is written by the
6
+ * module's `content:file:afterParse` hook.
7
+ */
8
+ declare const _default: any;
9
+ export default _default;
@@ -0,0 +1,34 @@
1
+ import * as contentRuntime from "#nuxtseo/content";
2
+ import { defineEventHandler } from "#nuxtseo/h3";
3
+ import { filters } from "#sitemap/content-filters";
4
+ import { onUrlFns } from "#sitemap/content-on-url";
5
+ const { listPageCollections, provider, queryPages } = contentRuntime;
6
+ export default defineEventHandler(async (e) => {
7
+ const collections = (await listPageCollections(e)).filter((collection) => collection.inSitemap && collection.hasField("sitemap")).map((collection) => collection.name);
8
+ const results = await Promise.all(collections.map(async (collection) => {
9
+ const needsAllFields = filters?.has(collection) || onUrlFns?.has(collection);
10
+ const query = queryPages(e, collection).where("path", "IS NOT NULL").where("sitemap", "IS NOT NULL");
11
+ if (!needsAllFields)
12
+ query.select("path", "sitemap");
13
+ try {
14
+ const entries = await query.all();
15
+ const filter = filters?.get(collection);
16
+ return { collection, entries: filter ? entries.filter(filter) : entries };
17
+ } catch (err) {
18
+ const hint = provider === "nuxt-content-v3" ? " On serverless the content DB is restored from a prerendered sql_dump.txt that isn't readable inside the function (nuxt/content#3805). Fix: prerender the sitemap so content URLs resolve at build, or configure a runtime database (D1/Turso/Postgres)." : "";
19
+ console.error(`[@nuxtjs/sitemap] Couldn't query content collection "${collection}" for the sitemap, so its URLs will be missing.${hint}`, err);
20
+ return { collection, entries: [] };
21
+ }
22
+ }));
23
+ return results.flatMap(({ collection, entries }) => {
24
+ const onUrl = onUrlFns?.get(collection);
25
+ return entries.filter((entry) => entry.sitemap !== false && entry.path && !entry.path.endsWith(".navigation")).map((entry) => {
26
+ const url = {
27
+ loc: entry.path,
28
+ ...typeof entry.sitemap === "object" && entry.sitemap ? entry.sitemap : {}
29
+ };
30
+ onUrl?.(url, entry, collection);
31
+ return url;
32
+ });
33
+ });
34
+ });
@@ -0,0 +1,7 @@
1
+ import type { AutoI18nConfig, ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl, SitemapDefinition, SitemapUrlInput } from '../../../types.js';
2
+ export interface NormalizedI18n extends ResolvedSitemapUrl {
3
+ _pathWithoutPrefix: string;
4
+ _locale: AutoI18nConfig['locales'][number];
5
+ _index?: number;
6
+ }
7
+ export declare function resolveSitemapEntries(sitemap: SitemapDefinition, urls: SitemapUrlInput[], runtimeConfig: Pick<ModuleRuntimeConfig, 'autoI18n' | 'isI18nMapped'>, resolvers?: NitroUrlResolvers, baseURL?: string): ResolvedSitemapUrl[];
@@ -0,0 +1,100 @@
1
+ import { createPathFilter, resolveI18nRouteEntries, splitForLocales } from "../../../utils-pure.js";
2
+ import { preNormalizeEntry } from "../urlset/normalise.js";
3
+ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers, baseURL) {
4
+ const {
5
+ autoI18n,
6
+ isI18nMapped
7
+ } = runtimeConfig;
8
+ const hasFilters = !!sitemap.include?.length || !!sitemap.exclude?.length;
9
+ const filterPath = hasFilters ? createPathFilter({
10
+ include: sitemap.include,
11
+ exclude: sitemap.exclude
12
+ }, baseURL || "/") : void 0;
13
+ const _urls = [];
14
+ for (const _e of urls) {
15
+ const e = preNormalizeEntry(_e, resolvers);
16
+ if (e.loc && (!filterPath || filterPath(e.loc, e._path?.pathname)))
17
+ _urls.push(e);
18
+ }
19
+ const withoutPrefixPaths = {};
20
+ if (autoI18n && autoI18n.strategy !== "no_prefix") {
21
+ const localeCodes = new Set(autoI18n.locales.map((l) => l.code));
22
+ const localeByCode = new Map(autoI18n.locales.map((l) => [l.code, l]));
23
+ const defaultLocale = autoI18n.defaultLocale;
24
+ const hasDifferentDomains = !!autoI18n.differentDomains;
25
+ const validI18nUrlsForTransform = [];
26
+ for (let i = 0; i < _urls.length; i++) {
27
+ const _e = _urls[i];
28
+ if (_e._abs)
29
+ continue;
30
+ const split = splitForLocales(_e._relativeLoc, localeCodes);
31
+ let localeCode = split[0];
32
+ const pathWithoutPrefix = split[1];
33
+ if (!localeCode)
34
+ localeCode = defaultLocale;
35
+ const e = _e;
36
+ e._pathWithoutPrefix = pathWithoutPrefix;
37
+ const locale = localeByCode.get(localeCode);
38
+ if (!locale)
39
+ continue;
40
+ e._locale = locale;
41
+ e._index = i;
42
+ e._key = `${e._sitemap || ""}${e._path?.pathname || "/"}${e._path?.search || ""}`;
43
+ withoutPrefixPaths[pathWithoutPrefix] = withoutPrefixPaths[pathWithoutPrefix] || [];
44
+ if (!withoutPrefixPaths[pathWithoutPrefix].some((e2) => e2._locale.code === locale.code))
45
+ withoutPrefixPaths[pathWithoutPrefix].push(e);
46
+ validI18nUrlsForTransform.push(e);
47
+ }
48
+ for (const e of validI18nUrlsForTransform) {
49
+ if (!e._i18nTransform && !e.alternatives?.length) {
50
+ const alternatives = [];
51
+ for (const u of withoutPrefixPaths[e._pathWithoutPrefix] || []) {
52
+ if (u._locale.code === defaultLocale) {
53
+ alternatives.push({
54
+ href: u.loc,
55
+ hreflang: "x-default"
56
+ });
57
+ }
58
+ alternatives.push({
59
+ href: u.loc,
60
+ hreflang: u._locale._hreflang || defaultLocale
61
+ });
62
+ }
63
+ if (alternatives.length)
64
+ e.alternatives = alternatives;
65
+ } else if (e._i18nTransform) {
66
+ delete e._i18nTransform;
67
+ const routeEntries = resolveI18nRouteEntries(e._relativeLoc, autoI18n, (href) => !filterPath || filterPath(href));
68
+ if (hasDifferentDomains) {
69
+ e.alternatives = routeEntries[0]?.alternatives;
70
+ } else {
71
+ for (const { alternatives, locale: l, loc } of routeEntries) {
72
+ const _sitemap = isI18nMapped ? l._sitemap : void 0;
73
+ const { _index: _, ...rest } = e;
74
+ const newEntry = preNormalizeEntry({
75
+ _sitemap,
76
+ ...rest,
77
+ _key: `${_sitemap || ""}${loc || "/"}`,
78
+ _locale: l,
79
+ loc,
80
+ alternatives
81
+ }, resolvers);
82
+ if (e._locale.code === newEntry._locale.code) {
83
+ _urls[e._index] = newEntry;
84
+ e._index = void 0;
85
+ } else {
86
+ _urls.push(newEntry);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ if (isI18nMapped) {
92
+ e._sitemap = e._sitemap || e._locale._sitemap;
93
+ e._key = `${e._sitemap || ""}${e.loc || "/"}${e._path?.search || ""}`;
94
+ }
95
+ if (e._index)
96
+ _urls[e._index] = e;
97
+ }
98
+ }
99
+ return _urls;
100
+ }
@@ -1,11 +1,5 @@
1
- import type { AutoI18nConfig, ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl, SitemapDefinition, SitemapUrlInput } from '../../../types.js';
1
+ import type { ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl, SitemapDefinition } from '../../../types.js';
2
2
  type NitroApp = ReturnType<typeof import('#nuxtseo/nitro').useNitroApp>;
3
- export interface NormalizedI18n extends ResolvedSitemapUrl {
4
- _pathWithoutPrefix: string;
5
- _locale: AutoI18nConfig['locales'][number];
6
- _index?: number;
7
- }
8
- export declare function resolveSitemapEntries(sitemap: SitemapDefinition, urls: SitemapUrlInput[], runtimeConfig: Pick<ModuleRuntimeConfig, 'autoI18n' | 'isI18nMapped'>, resolvers?: NitroUrlResolvers, baseURL?: string): ResolvedSitemapUrl[];
9
3
  export interface ResolvedSitemapUrlsResult {
10
4
  urls: ResolvedSitemapUrl[];
11
5
  failedSources: Array<{
@@ -1,172 +1,14 @@
1
1
  import { resolveSitePath } from "nuxt-site-config/urls";
2
- import { joinURL, withHttps } from "ufo";
2
+ import { withHttps } from "ufo";
3
3
  import { getHeader } from "#nuxtseo/h3";
4
4
  import { defineCachedFunction, useRuntimeConfig } from "#nuxtseo/nitro";
5
5
  import staticConfig from "#sitemap-virtual/static-config.mjs";
6
- import { applyDynamicParams, createPathFilter, findPageMapping, logger, resolveI18nSitemapLocaleKey, splitForLocales } from "../../../utils-pure.js";
7
- import { preNormalizeEntry } from "../urlset/normalise.js";
6
+ import { logger, resolveI18nSitemapLocaleKey } from "../../../utils-pure.js";
8
7
  import { sortInPlace } from "../urlset/sort.js";
9
8
  import { childSitemapSources, globalSitemapSources, resolveSitemapSources } from "../urlset/sources.js";
10
9
  import { parseChunkInfo, sliceUrlsForChunk } from "../utils/chunk.js";
10
+ import { resolveSitemapEntries } from "./entries.js";
11
11
  const SERVER_CACHE_MAX_AGE = staticConfig.cacheMaxAgeSeconds || 60 * 10;
12
- export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers, baseURL) {
13
- const {
14
- autoI18n,
15
- isI18nMapped
16
- } = runtimeConfig;
17
- const hasFilters = !!sitemap.include?.length || !!sitemap.exclude?.length;
18
- const filterPath = hasFilters ? createPathFilter({
19
- include: sitemap.include,
20
- exclude: sitemap.exclude
21
- }, baseURL || "/") : void 0;
22
- const _urls = [];
23
- for (const _e of urls) {
24
- const e = preNormalizeEntry(_e, resolvers);
25
- if (e.loc && (!filterPath || filterPath(e.loc, e._path?.pathname)))
26
- _urls.push(e);
27
- }
28
- const withoutPrefixPaths = {};
29
- if (autoI18n && autoI18n.strategy !== "no_prefix") {
30
- const localeCodes = new Set(autoI18n.locales.map((l) => l.code));
31
- const localeByCode = new Map(autoI18n.locales.map((l) => [l.code, l]));
32
- const isPrefixStrategy = autoI18n.strategy === "prefix";
33
- const isPrefixExceptOrAndDefault = autoI18n.strategy === "prefix_and_default" || autoI18n.strategy === "prefix_except_default";
34
- const xDefaultAndLocales = [{ code: "x-default", _hreflang: "x-default" }, ...autoI18n.locales];
35
- const defaultLocale = autoI18n.defaultLocale;
36
- const hasPages = !!autoI18n.pages;
37
- const sortedPageKeys = hasPages ? Object.keys(autoI18n.pages).sort((a, b) => b.length - a.length) : void 0;
38
- const hasDifferentDomains = !!autoI18n.differentDomains;
39
- const validI18nUrlsForTransform = [];
40
- for (let i = 0; i < _urls.length; i++) {
41
- const _e = _urls[i];
42
- if (_e._abs)
43
- continue;
44
- const split = splitForLocales(_e._relativeLoc, localeCodes);
45
- let localeCode = split[0];
46
- const pathWithoutPrefix = split[1];
47
- if (!localeCode)
48
- localeCode = defaultLocale;
49
- const e = _e;
50
- e._pathWithoutPrefix = pathWithoutPrefix;
51
- const locale = localeByCode.get(localeCode);
52
- if (!locale)
53
- continue;
54
- e._locale = locale;
55
- e._index = i;
56
- e._key = `${e._sitemap || ""}${e._path?.pathname || "/"}${e._path?.search || ""}`;
57
- withoutPrefixPaths[pathWithoutPrefix] = withoutPrefixPaths[pathWithoutPrefix] || [];
58
- if (!withoutPrefixPaths[pathWithoutPrefix].some((e2) => e2._locale.code === locale.code))
59
- withoutPrefixPaths[pathWithoutPrefix].push(e);
60
- validI18nUrlsForTransform.push(e);
61
- }
62
- for (const e of validI18nUrlsForTransform) {
63
- if (!e._i18nTransform && !e.alternatives?.length) {
64
- const alternatives = [];
65
- for (const u of withoutPrefixPaths[e._pathWithoutPrefix] || []) {
66
- if (u._locale.code === defaultLocale) {
67
- alternatives.push({
68
- href: u.loc,
69
- hreflang: "x-default"
70
- });
71
- }
72
- alternatives.push({
73
- href: u.loc,
74
- hreflang: u._locale._hreflang || defaultLocale
75
- });
76
- }
77
- if (alternatives.length)
78
- e.alternatives = alternatives;
79
- } else if (e._i18nTransform) {
80
- delete e._i18nTransform;
81
- if (hasDifferentDomains) {
82
- const defLocale = localeByCode.get(defaultLocale);
83
- e.alternatives = [
84
- {
85
- ...defLocale,
86
- code: "x-default"
87
- },
88
- ...autoI18n.locales.filter((l) => !!l.domain)
89
- ].map((locale) => {
90
- return {
91
- hreflang: locale._hreflang,
92
- href: joinURL(withHttps(locale.domain), e._pathWithoutPrefix)
93
- };
94
- });
95
- } else {
96
- const pageMatch = hasPages ? findPageMapping(e._pathWithoutPrefix, autoI18n.pages, sortedPageKeys) : null;
97
- const pathSearch = e._path?.search || "";
98
- const pathWithoutPrefix = e._pathWithoutPrefix;
99
- for (const l of autoI18n.locales) {
100
- let loc = pathWithoutPrefix;
101
- if (pageMatch && pageMatch.mappings[l.code] !== void 0) {
102
- const customPath = pageMatch.mappings[l.code];
103
- if (customPath === false)
104
- continue;
105
- if (typeof customPath === "string") {
106
- loc = customPath[0] === "/" ? customPath : `/${customPath}`;
107
- loc = applyDynamicParams(loc, pageMatch.paramSegments);
108
- if (isPrefixStrategy || isPrefixExceptOrAndDefault && l.code !== defaultLocale)
109
- loc = joinURL(`/${l.code}`, loc);
110
- }
111
- } else if (!hasDifferentDomains && !(isPrefixExceptOrAndDefault && l.code === defaultLocale)) {
112
- loc = joinURL(`/${l.code}`, pathWithoutPrefix);
113
- }
114
- const _sitemap = isI18nMapped ? l._sitemap : void 0;
115
- const alternatives = [];
116
- for (const locale of xDefaultAndLocales) {
117
- const code = locale.code === "x-default" ? defaultLocale : locale.code;
118
- const isDefault = locale.code === "x-default" || locale.code === defaultLocale;
119
- let href = pathWithoutPrefix;
120
- if (pageMatch && pageMatch.mappings[code] !== void 0) {
121
- const customPath = pageMatch.mappings[code];
122
- if (customPath === false)
123
- continue;
124
- if (typeof customPath === "string") {
125
- href = customPath[0] === "/" ? customPath : `/${customPath}`;
126
- href = applyDynamicParams(href, pageMatch.paramSegments);
127
- if (isPrefixStrategy || isPrefixExceptOrAndDefault && !isDefault)
128
- href = joinURL("/", code, href);
129
- }
130
- } else if (isPrefixStrategy) {
131
- href = joinURL("/", code, pathWithoutPrefix);
132
- } else if (isPrefixExceptOrAndDefault && !isDefault) {
133
- href = joinURL("/", code, pathWithoutPrefix);
134
- }
135
- if (filterPath && !filterPath(href))
136
- continue;
137
- alternatives.push({
138
- hreflang: locale._hreflang,
139
- href
140
- });
141
- }
142
- const { _index: _, ...rest } = e;
143
- const newEntry = preNormalizeEntry({
144
- _sitemap,
145
- ...rest,
146
- _key: `${_sitemap || ""}${loc || "/"}${pathSearch}`,
147
- _locale: l,
148
- loc,
149
- alternatives
150
- }, resolvers);
151
- if (e._locale.code === newEntry._locale.code) {
152
- _urls[e._index] = newEntry;
153
- e._index = void 0;
154
- } else {
155
- _urls.push(newEntry);
156
- }
157
- }
158
- }
159
- }
160
- if (isI18nMapped) {
161
- e._sitemap = e._sitemap || e._locale._sitemap;
162
- e._key = `${e._sitemap || ""}${e.loc || "/"}${e._path?.search || ""}`;
163
- }
164
- if (e._index)
165
- _urls[e._index] = e;
166
- }
167
- }
168
- return _urls;
169
- }
170
12
  export async function buildResolvedSitemapUrls(effectiveSitemap, matchName, isChunked, resolvers, runtimeConfig, nitro) {
171
13
  const { sitemaps, autoI18n, isI18nMapped, isMultiSitemap, sortEntries } = runtimeConfig;
172
14
  let sourcesInput = effectiveSitemap.includeAppSources ? [...await globalSitemapSources(), ...await childSitemapSources(effectiveSitemap)] : await childSitemapSources(effectiveSitemap);
@@ -204,7 +204,7 @@ export interface SitemapSourceResolved extends Omit<SitemapSourceBase, 'urls'> {
204
204
  message: string;
205
205
  }[];
206
206
  }
207
- export type AppSourceContext = 'nuxt:pages' | 'nuxt:prerender' | 'nuxt:route-rules' | '@nuxtjs/i18n:pages' | 'nuxt-i18n-micro:pages' | '@nuxt/content@v2:urls' | '@nuxt/content@v3:urls';
207
+ export type AppSourceContext = 'nuxt:pages' | 'nuxt:prerender' | 'nuxt:route-rules' | '@nuxtjs/i18n:pages' | 'nuxt-i18n-micro:pages' | '@nuxt/content@v2:urls' | '@nuxt/content@v3:urls' | '@harlan-zw/comark-content:urls';
208
208
  export type SitemapSourceInput = string | [string, FetchOptions] | SitemapSourceBase | SitemapSourceResolved;
209
209
  interface LocaleObject extends Record<string, any> {
210
210
  code: string;
@@ -1,9 +1,15 @@
1
- import type { FilterInput } from './types.js';
1
+ import type { AlternativeEntry, AutoI18nConfig, FilterInput } from './types.js';
2
2
  export { createFilter, type CreateFilterOptions } from 'nuxtseo-shared/utils';
3
3
  export declare const logger: import("consola").ConsolaInstance;
4
4
  export declare function xmlEscape(value: string | number | boolean | Date): string;
5
5
  export declare function mergeOnKey<T, K extends keyof T>(arr: T[], key: K, onMerge?: (key: T[K]) => void): T[];
6
6
  export declare function splitForLocales(path: string, locales: readonly string[] | Set<string>): [string | null, string];
7
+ export interface ResolvedI18nRouteEntry {
8
+ locale: AutoI18nConfig['locales'][number];
9
+ loc: string;
10
+ alternatives: AlternativeEntry[];
11
+ }
12
+ export declare function resolveI18nRouteEntries(route: string, i18n: AutoI18nConfig, includeHref?: (href: string) => boolean): ResolvedI18nRouteEntry[];
7
13
  /**
8
14
  * Resolve which locale a multi-sitemap name belongs to.
9
15
  *
@@ -22,9 +28,3 @@ export declare function createPathFilter(options?: {
22
28
  include?: (FilterInput | string | RegExp)[];
23
29
  exclude?: (FilterInput | string | RegExp)[];
24
30
  }, baseURL?: string): (loc: string, pathname?: string) => boolean;
25
- export interface PageMatch {
26
- mappings: Record<string, string | false>;
27
- paramSegments: string[];
28
- }
29
- export declare function findPageMapping(pathWithoutPrefix: string, pages: Record<string, Record<string, string | false>>, sortedKeys?: string[]): PageMatch | null;
30
- export declare function applyDynamicParams(customPath: string, paramSegments: string[]): string;
@@ -1,6 +1,7 @@
1
1
  import { createDefu } from "defu";
2
+ import { computeLocaleAlternates, resolveLocaleFromRoute } from "nuxtseo-shared/i18n-runtime";
2
3
  import { createFilter, createModuleLogger } from "nuxtseo-shared/utils";
3
- import { parseURL, withoutBase } from "ufo";
4
+ import { joinURL, parseURL, withHttps, withLeadingSlash, withoutBase } from "ufo";
4
5
  export { createFilter } from "nuxtseo-shared/utils";
5
6
  export const logger = createModuleLogger("@nuxt/sitemap");
6
7
  const XML_ENTITIES = {
@@ -51,6 +52,47 @@ export function splitForLocales(path, locales) {
51
52
  }
52
53
  return [null, path];
53
54
  }
55
+ function toRuntimeI18nConfig(i18n) {
56
+ return {
57
+ ...i18n,
58
+ // Sitemap transforms keep the unprefixed default URL alongside Nuxt's prefixed route.
59
+ strategy: i18n.strategy === "prefix_and_default" ? "prefix_except_default" : i18n.strategy,
60
+ pages: i18n.pages && Object.fromEntries(
61
+ Object.entries(i18n.pages).map(([pageName, pageLocales]) => [
62
+ pageName,
63
+ Object.fromEntries(i18n.locales.map((locale) => {
64
+ const configuredPath = pageLocales[locale.code];
65
+ return [locale.code, configuredPath === void 0 ? withLeadingSlash(pageName) : configuredPath];
66
+ }))
67
+ ])
68
+ ),
69
+ locales: i18n.locales.map((locale) => ({
70
+ ...locale,
71
+ hreflang: locale._hreflang
72
+ }))
73
+ };
74
+ }
75
+ function localeAlternateHref(alternate) {
76
+ return alternate.domain ? joinURL(withHttps(alternate.domain), alternate.path) : alternate.path;
77
+ }
78
+ export function resolveI18nRouteEntries(route, i18n, includeHref = () => true) {
79
+ const runtimeConfig = toRuntimeI18nConfig(i18n);
80
+ const currentLocale = resolveLocaleFromRoute(route, runtimeConfig).locale;
81
+ const alternates = computeLocaleAlternates(route, runtimeConfig, { locale: currentLocale });
82
+ const localizedAlternates = alternates.map((alternate) => ({
83
+ alternate,
84
+ href: localeAlternateHref(alternate)
85
+ }));
86
+ const defaultHref = localizedAlternates.find(({ alternate }) => alternate.code === i18n.defaultLocale)?.href;
87
+ const sitemapAlternatives = [
88
+ ...defaultHref && includeHref(defaultHref) ? [{ hreflang: "x-default", href: defaultHref }] : [],
89
+ ...localizedAlternates.filter(({ href }) => includeHref(href)).map(({ alternate, href }) => ({ hreflang: alternate.hreflang, href }))
90
+ ];
91
+ return localizedAlternates.flatMap(({ alternate, href }) => {
92
+ const locale = i18n.locales.find((locale2) => locale2.code === alternate.code);
93
+ return locale ? [{ locale, loc: href, alternatives: sitemapAlternatives }] : [];
94
+ });
95
+ }
54
96
  export function resolveI18nSitemapLocaleKey(sitemapName, localeSitemapKeys) {
55
97
  let best = null;
56
98
  for (const key of localeSitemapKeys) {
@@ -89,26 +131,6 @@ export function createPathFilter(options = {}, baseURL) {
89
131
  }
90
132
  if (hasBase)
91
133
  path = withoutBase(path, baseURL);
92
- return urlFilter(path);
134
+ return urlFilter(withLeadingSlash(path));
93
135
  };
94
136
  }
95
- export function findPageMapping(pathWithoutPrefix, pages, sortedKeys) {
96
- const stripped = pathWithoutPrefix[0] === "/" ? pathWithoutPrefix.slice(1) : pathWithoutPrefix;
97
- const pageKey = stripped.endsWith("/index") ? stripped.slice(0, -6) || "index" : stripped || "index";
98
- if (pages[pageKey])
99
- return { mappings: pages[pageKey], paramSegments: [] };
100
- const keys = sortedKeys || Object.keys(pages).sort((a, b) => b.length - a.length);
101
- for (const key of keys) {
102
- if (pageKey.startsWith(`${key}/`)) {
103
- const paramPath = pageKey.slice(key.length + 1);
104
- return { mappings: pages[key], paramSegments: paramPath.split("/") };
105
- }
106
- }
107
- return null;
108
- }
109
- export function applyDynamicParams(customPath, paramSegments) {
110
- if (!paramSegments.length)
111
- return customPath;
112
- let i = 0;
113
- return customPath.replace(/\[[^\]]+\]/g, () => paramSegments[i++] || "");
114
- }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nuxtjs/sitemap",
3
3
  "type": "module",
4
- "version": "8.3.3",
4
+ "version": "8.4.0",
5
5
  "description": "Powerfully flexible XML Sitemaps that integrate seamlessly, for Nuxt.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -55,45 +55,46 @@
55
55
  }
56
56
  },
57
57
  "dependencies": {
58
- "@nuxt/kit": "^4.5.1",
58
+ "@nuxt/kit": "^4.5.2",
59
59
  "consola": "^3.4.2",
60
60
  "defu": "^6.1.7",
61
- "nuxt-site-config": "^4.1.5",
62
- "nuxtseo-shared": "^5.3.10",
61
+ "nuxt-site-config": "^4.2.3",
62
+ "nuxtseo-shared": "^5.3.14",
63
63
  "ofetch": "^1.5.1",
64
64
  "pathe": "^2.0.3",
65
65
  "pkg-types": "^2.3.1",
66
66
  "radix3": "^1.1.2",
67
67
  "ufo": "^1.6.4",
68
68
  "ultrahtml": "^1.7.0",
69
- "sitemapd": "^0.2.1"
69
+ "sitemapd": "^0.2.2"
70
70
  },
71
71
  "devDependencies": {
72
- "@antfu/eslint-config": "^9.2.0",
72
+ "@antfu/eslint-config": "^9.3.0",
73
73
  "@arethetypeswrong/cli": "^0.18.5",
74
+ "@harlan-zw/comark-content": "^0.1.3",
74
75
  "@nuxt/content": "^3.15.2",
75
- "@nuxt/devtools-kit": "4.0.0-alpha.9",
76
+ "@nuxt/devtools-kit": "4.0.0-alpha.11",
76
77
  "@nuxt/module-builder": "^1.0.3",
77
78
  "@nuxt/test-utils": "^4.1.0",
78
79
  "@nuxt/ui": "^4.10.0",
79
80
  "@nuxtjs/i18n": "^10.6.0",
80
- "@nuxtjs/robots": "^6.1.3",
81
+ "@nuxtjs/robots": "^6.1.5",
81
82
  "@vue/test-utils": "^2.4.11",
82
- "better-sqlite3": "^13.0.2",
83
- "bumpp": "^12.1.1",
84
- "eslint": "^10.8.0",
85
- "eslint-plugin-harlanzw": "^0.17.1",
86
- "happy-dom": "^20.11.1",
87
- "nuxt": "^4.5.1",
88
- "nuxt-i18n-micro": "^3.26.0",
89
- "nuxtseo-layer-devtools": "^5.3.10",
83
+ "better-sqlite3": "^13.0.3",
84
+ "bumpp": "^12.2.1",
85
+ "eslint": "^10.8.1",
86
+ "eslint-plugin-harlanzw": "^0.20.0",
87
+ "happy-dom": "^20.11.2",
88
+ "nuxt": "^4.5.2",
89
+ "nuxt-i18n-micro": "^3.28.0",
90
+ "nuxtseo-layer-devtools": "^5.3.14",
90
91
  "std-env": "^4.2.0",
91
92
  "typescript": "6.0.3",
92
93
  "unbuild": "^3.6.1",
93
- "vitest": "^4.1.10",
94
- "vue-tsc": "^3.3.9",
94
+ "vitest": "^4.1.11",
95
+ "vue-tsc": "^3.3.10",
95
96
  "zod": "^4.4.3",
96
- "@nuxtjs/sitemap": "8.3.3"
97
+ "@nuxtjs/sitemap": "8.4.0"
97
98
  },
98
99
  "scripts": {
99
100
  "lint": "eslint .",
@@ -1,2 +0,0 @@
1
- declare const _default: any;
2
- export default _default;
@@ -1,40 +0,0 @@
1
- import { queryCollection } from "@nuxt/content/server";
2
- import manifest from "#content/manifest";
3
- import { defineEventHandler } from "#nuxtseo/h3";
4
- import { filters } from "#sitemap/content-filters";
5
- import { onUrlFns } from "#sitemap/content-on-url";
6
- export default defineEventHandler(async (e) => {
7
- const collections = [];
8
- for (const collection in manifest) {
9
- if (manifest[collection].fields.sitemap)
10
- collections.push(collection);
11
- }
12
- const contentList = [];
13
- for (const collection of collections) {
14
- const needsAllFields = filters?.has(collection) || onUrlFns?.has(collection);
15
- const query = queryCollection(e, collection).where("path", "IS NOT NULL").where("sitemap", "IS NOT NULL");
16
- if (!needsAllFields)
17
- query.select("path", "sitemap");
18
- contentList.push(
19
- query.all().then((results2) => {
20
- const filter = filters?.get(collection);
21
- return { collection, entries: filter ? results2.filter(filter) : results2 };
22
- }).catch((err) => {
23
- console.error(`[@nuxtjs/sitemap] Couldn't query @nuxt/content collection "${collection}" for the sitemap, so its URLs will be missing. On serverless the content DB is restored from a prerendered sql_dump.txt that isn't readable inside the function (nuxt/content#3805). Fix: prerender the sitemap so content URLs resolve at build, or configure a runtime database (D1/Turso/Postgres).`, err);
24
- return { collection, entries: [] };
25
- })
26
- );
27
- }
28
- const results = await Promise.all(contentList);
29
- return results.flatMap(({ collection, entries }) => {
30
- const onUrl = onUrlFns?.get(collection);
31
- return entries.filter((c) => c.sitemap !== false && c.path && !c.path.endsWith(".navigation")).map((c) => {
32
- const url = {
33
- loc: c.path,
34
- ...typeof c.sitemap === "object" ? c.sitemap : {}
35
- };
36
- onUrl?.(url, c, collection);
37
- return url;
38
- });
39
- }).filter(Boolean);
40
- });