@nuxtjs/sitemap 7.4.5 → 7.4.7
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.
- package/dist/client/200.html +1 -0
- package/dist/client/404.html +1 -0
- package/dist/client/__sitemap__/style.xsl +190 -0
- package/dist/client/_nuxt/CVMmb_pX.js +1 -0
- package/dist/client/_nuxt/CVO1_9PV.js +1 -0
- package/dist/client/_nuxt/Cp-IABpG.js +1 -0
- package/dist/client/_nuxt/D0r3Knsf.js +1 -0
- package/dist/client/_nuxt/Uwg2rjhu.js +172 -0
- package/dist/client/_nuxt/builds/latest.json +1 -0
- package/dist/client/_nuxt/builds/meta/d2f0f6ff-dbbc-45cd-bac8-0f24a0d6c572.json +1 -0
- package/dist/client/_nuxt/entry.D5V0t8Hh.css +1 -0
- package/dist/client/_nuxt/error-404.BXx3NK2Z.css +1 -0
- package/dist/client/_nuxt/error-500.BYrbHUTO.css +1 -0
- package/dist/client/_nuxt/rHMUQZkV.js +1 -0
- package/dist/client/index.html +1 -0
- package/dist/client/sitemap.xml +7 -0
- package/dist/content.d.mts +232 -0
- package/dist/content.d.ts +232 -0
- package/dist/content.mjs +45 -0
- package/dist/module.d.mts +10 -0
- package/dist/module.d.ts +10 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +1221 -0
- package/dist/runtime/server/composables/asSitemapUrl.d.ts +2 -0
- package/dist/runtime/server/composables/asSitemapUrl.js +3 -0
- package/dist/runtime/server/composables/defineSitemapEventHandler.d.ts +4 -0
- package/dist/runtime/server/composables/defineSitemapEventHandler.js +2 -0
- package/dist/runtime/server/content-compat.d.ts +1 -0
- package/dist/runtime/server/content-compat.js +2 -0
- package/dist/runtime/server/kit.d.ts +3 -0
- package/dist/runtime/server/kit.js +25 -0
- package/dist/runtime/server/plugins/compression.d.ts +2 -0
- package/dist/runtime/server/plugins/compression.js +8 -0
- package/dist/runtime/server/plugins/nuxt-content-v2.d.ts +2 -0
- package/dist/runtime/server/plugins/nuxt-content-v2.js +39 -0
- package/dist/runtime/server/plugins/warm-up.d.ts +2 -0
- package/dist/runtime/server/plugins/warm-up.js +39 -0
- package/dist/runtime/server/robots-polyfill/getPathRobotConfig.d.ts +5 -0
- package/dist/runtime/server/robots-polyfill/getPathRobotConfig.js +3 -0
- package/dist/runtime/server/routes/__sitemap__/debug.d.ts +40 -0
- package/dist/runtime/server/routes/__sitemap__/debug.js +29 -0
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v2.d.ts +2 -0
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v2.js +6 -0
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.d.ts +2 -0
- package/dist/runtime/server/routes/__sitemap__/nuxt-content-urls-v3.js +24 -0
- package/dist/runtime/server/routes/sitemap/[sitemap].xml.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap/[sitemap].xml.js +50 -0
- package/dist/runtime/server/routes/sitemap.xml.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap.xml.js +13 -0
- package/dist/runtime/server/routes/sitemap.xsl.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap.xsl.js +255 -0
- package/dist/runtime/server/routes/sitemap_index.xml.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap_index.xml.js +42 -0
- package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +13 -0
- package/dist/runtime/server/sitemap/builder/sitemap-index.js +197 -0
- package/dist/runtime/server/sitemap/builder/sitemap.d.ts +16 -0
- package/dist/runtime/server/sitemap/builder/sitemap.js +241 -0
- package/dist/runtime/server/sitemap/builder/xml.d.ts +6 -0
- package/dist/runtime/server/sitemap/builder/xml.js +197 -0
- package/dist/runtime/server/sitemap/nitro.d.ts +4 -0
- package/dist/runtime/server/sitemap/nitro.js +144 -0
- package/dist/runtime/server/sitemap/urlset/normalise.d.ts +6 -0
- package/dist/runtime/server/sitemap/urlset/normalise.js +137 -0
- package/dist/runtime/server/sitemap/urlset/sort.d.ts +2 -0
- package/dist/runtime/server/sitemap/urlset/sort.js +13 -0
- package/dist/runtime/server/sitemap/urlset/sources.d.ts +6 -0
- package/dist/runtime/server/sitemap/urlset/sources.js +143 -0
- package/dist/runtime/server/sitemap/utils/chunk.d.ts +10 -0
- package/dist/runtime/server/sitemap/utils/chunk.js +67 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/server/utils.d.ts +5 -0
- package/dist/runtime/server/utils.js +16 -0
- package/dist/runtime/types.d.ts +458 -0
- package/dist/runtime/types.js +0 -0
- package/dist/runtime/utils-pure.d.ts +14 -0
- package/dist/runtime/utils-pure.js +85 -0
- package/dist/shared/sitemap.DR3_6qqU.mjs +212 -0
- package/dist/types.d.mts +5 -0
- package/dist/utils.d.mts +28 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.mjs +368 -0
- package/package.json +2 -2
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { appendHeader, defineEventHandler, setHeader } from "h3";
|
|
2
|
+
import { joinURL } from "ufo";
|
|
3
|
+
import { useNitroApp } from "nitropack/runtime";
|
|
4
|
+
import { useSitemapRuntimeConfig } from "../utils.js";
|
|
5
|
+
import { buildSitemapIndex, urlsToIndexXml } from "../sitemap/builder/sitemap-index.js";
|
|
6
|
+
import { useNitroUrlResolvers } from "../sitemap/nitro.js";
|
|
7
|
+
export default defineEventHandler(async (e) => {
|
|
8
|
+
const runtimeConfig = useSitemapRuntimeConfig();
|
|
9
|
+
const nitro = useNitroApp();
|
|
10
|
+
const resolvers = useNitroUrlResolvers(e);
|
|
11
|
+
const { entries: sitemaps, failedSources } = await buildSitemapIndex(resolvers, runtimeConfig, nitro);
|
|
12
|
+
if (import.meta.prerender) {
|
|
13
|
+
appendHeader(
|
|
14
|
+
e,
|
|
15
|
+
"x-nitro-prerender",
|
|
16
|
+
sitemaps.filter((entry) => !!entry._sitemapName).map((entry) => encodeURIComponent(joinURL(runtimeConfig.sitemapsPathPrefix || "", `/${entry._sitemapName}.xml`))).join(", ")
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const indexResolvedCtx = { sitemaps, event: e };
|
|
20
|
+
await nitro.hooks.callHook("sitemap:index-resolved", indexResolvedCtx);
|
|
21
|
+
const errorInfo = failedSources.length > 0 ? {
|
|
22
|
+
messages: failedSources.map((f) => f.error),
|
|
23
|
+
urls: failedSources.map((f) => f.url)
|
|
24
|
+
} : void 0;
|
|
25
|
+
const output = urlsToIndexXml(indexResolvedCtx.sitemaps, resolvers, runtimeConfig, errorInfo);
|
|
26
|
+
const ctx = { sitemap: output, sitemapName: "sitemap", event: e };
|
|
27
|
+
await nitro.hooks.callHook("sitemap:output", ctx);
|
|
28
|
+
setHeader(e, "Content-Type", "text/xml; charset=UTF-8");
|
|
29
|
+
if (runtimeConfig.cacheMaxAgeSeconds) {
|
|
30
|
+
setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
|
|
31
|
+
const now = /* @__PURE__ */ new Date();
|
|
32
|
+
setHeader(e, "X-Sitemap-Generated", now.toISOString());
|
|
33
|
+
setHeader(e, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
|
|
34
|
+
const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
|
|
35
|
+
setHeader(e, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
|
|
36
|
+
const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
|
|
37
|
+
setHeader(e, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
|
|
38
|
+
} else {
|
|
39
|
+
setHeader(e, "Cache-Control", `no-cache, no-store`);
|
|
40
|
+
}
|
|
41
|
+
return ctx.sitemap;
|
|
42
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NitroApp } from 'nitropack/types';
|
|
2
|
+
import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapIndexEntry } from '../../../types.js';
|
|
3
|
+
export declare function urlsToIndexXml(sitemaps: SitemapIndexEntry[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
4
|
+
messages: string[];
|
|
5
|
+
urls: string[];
|
|
6
|
+
}): string;
|
|
7
|
+
export declare function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<{
|
|
8
|
+
entries: SitemapIndexEntry[];
|
|
9
|
+
failedSources: Array<{
|
|
10
|
+
url: string;
|
|
11
|
+
error: string;
|
|
12
|
+
}>;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import { joinURL, withQuery } from "ufo";
|
|
3
|
+
import { defineCachedFunction } from "nitropack/runtime";
|
|
4
|
+
import { getHeader } from "h3";
|
|
5
|
+
import { normaliseDate } from "../urlset/normalise.js";
|
|
6
|
+
import { globalSitemapSources, childSitemapSources, resolveSitemapSources } from "../urlset/sources.js";
|
|
7
|
+
import { sortInPlace } from "../urlset/sort.js";
|
|
8
|
+
import { escapeValueForXml } from "./xml.js";
|
|
9
|
+
import { resolveSitemapEntries } from "./sitemap.js";
|
|
10
|
+
const buildSitemapIndexCached = defineCachedFunction(
|
|
11
|
+
async (event, resolvers, runtimeConfig, nitro) => {
|
|
12
|
+
return buildSitemapIndexInternal(resolvers, runtimeConfig, nitro);
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "sitemap:index",
|
|
16
|
+
group: "sitemap",
|
|
17
|
+
maxAge: 60 * 10,
|
|
18
|
+
// 10 minutes default
|
|
19
|
+
base: "sitemap",
|
|
20
|
+
// Use the sitemap storage
|
|
21
|
+
getKey: (event) => {
|
|
22
|
+
const host = getHeader(event, "host") || getHeader(event, "x-forwarded-host") || "";
|
|
23
|
+
const proto = getHeader(event, "x-forwarded-proto") || "https";
|
|
24
|
+
return `sitemap-index-${proto}-${host}`;
|
|
25
|
+
},
|
|
26
|
+
swr: true
|
|
27
|
+
// Enable stale-while-revalidate
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
async function buildSitemapIndexInternal(resolvers, runtimeConfig, nitro) {
|
|
31
|
+
const {
|
|
32
|
+
sitemaps,
|
|
33
|
+
// enhancing
|
|
34
|
+
autoLastmod,
|
|
35
|
+
// chunking
|
|
36
|
+
defaultSitemapsChunkSize,
|
|
37
|
+
autoI18n,
|
|
38
|
+
isI18nMapped,
|
|
39
|
+
sortEntries,
|
|
40
|
+
sitemapsPathPrefix
|
|
41
|
+
} = runtimeConfig;
|
|
42
|
+
if (!sitemaps)
|
|
43
|
+
throw new Error("Attempting to build a sitemap index without required `sitemaps` configuration.");
|
|
44
|
+
function maybeSort(urls) {
|
|
45
|
+
return sortEntries ? sortInPlace(urls) : urls;
|
|
46
|
+
}
|
|
47
|
+
const chunks = {};
|
|
48
|
+
const allFailedSources = [];
|
|
49
|
+
for (const sitemapName in sitemaps) {
|
|
50
|
+
if (sitemapName === "index" || sitemapName === "chunks") continue;
|
|
51
|
+
const sitemapConfig = sitemaps[sitemapName];
|
|
52
|
+
if (sitemapConfig.chunks || sitemapConfig._isChunking) {
|
|
53
|
+
sitemapConfig._isChunking = true;
|
|
54
|
+
sitemapConfig._chunkSize = typeof sitemapConfig.chunks === "number" ? sitemapConfig.chunks : sitemapConfig.chunkSize || defaultSitemapsChunkSize || 1e3;
|
|
55
|
+
} else {
|
|
56
|
+
chunks[sitemapName] = chunks[sitemapName] || { urls: [] };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (typeof sitemaps.chunks !== "undefined") {
|
|
60
|
+
const sitemap = sitemaps.chunks;
|
|
61
|
+
let sourcesInput = await globalSitemapSources();
|
|
62
|
+
if (nitro && resolvers.event) {
|
|
63
|
+
const ctx = {
|
|
64
|
+
event: resolvers.event,
|
|
65
|
+
sitemapName: sitemap.sitemapName,
|
|
66
|
+
sources: sourcesInput
|
|
67
|
+
};
|
|
68
|
+
await nitro.hooks.callHook("sitemap:sources", ctx);
|
|
69
|
+
sourcesInput = ctx.sources;
|
|
70
|
+
}
|
|
71
|
+
const sources = await resolveSitemapSources(sourcesInput, resolvers.event);
|
|
72
|
+
const failedSources = sources.filter((source) => source.error && source._isFailure).map((source) => ({
|
|
73
|
+
url: typeof source.fetch === "string" ? source.fetch : source.fetch?.[0] || "unknown",
|
|
74
|
+
error: source.error || "Unknown error"
|
|
75
|
+
}));
|
|
76
|
+
allFailedSources.push(...failedSources);
|
|
77
|
+
const resolvedCtx = {
|
|
78
|
+
urls: sources.flatMap((s) => s.urls),
|
|
79
|
+
sitemapName: sitemap.sitemapName,
|
|
80
|
+
event: resolvers.event
|
|
81
|
+
};
|
|
82
|
+
await nitro?.hooks.callHook("sitemap:input", resolvedCtx);
|
|
83
|
+
const normalisedUrls = resolveSitemapEntries(sitemap, resolvedCtx.urls, { autoI18n, isI18nMapped }, resolvers);
|
|
84
|
+
const enhancedUrls = normalisedUrls.map((e) => defu(e, sitemap.defaults));
|
|
85
|
+
const sortedUrls = maybeSort(enhancedUrls);
|
|
86
|
+
sortedUrls.forEach((url, i) => {
|
|
87
|
+
const chunkIndex = Math.floor(i / defaultSitemapsChunkSize);
|
|
88
|
+
chunks[chunkIndex] = chunks[chunkIndex] || { urls: [] };
|
|
89
|
+
chunks[chunkIndex].urls.push(url);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const entries = [];
|
|
93
|
+
for (const name in chunks) {
|
|
94
|
+
const sitemap = chunks[name];
|
|
95
|
+
const entry = {
|
|
96
|
+
_sitemapName: name,
|
|
97
|
+
sitemap: resolvers.canonicalUrlResolver(joinURL(sitemapsPathPrefix || "", `/${name}.xml`))
|
|
98
|
+
};
|
|
99
|
+
let lastmod = sitemap.urls.filter((a) => !!a?.lastmod).map((a) => typeof a.lastmod === "string" ? new Date(a.lastmod) : a.lastmod).sort((a, b) => (b?.getTime() || 0) - (a?.getTime() || 0))?.[0];
|
|
100
|
+
if (!lastmod && autoLastmod)
|
|
101
|
+
lastmod = /* @__PURE__ */ new Date();
|
|
102
|
+
if (lastmod)
|
|
103
|
+
entry.lastmod = normaliseDate(lastmod);
|
|
104
|
+
entries.push(entry);
|
|
105
|
+
}
|
|
106
|
+
for (const sitemapName in sitemaps) {
|
|
107
|
+
const sitemapConfig = sitemaps[sitemapName];
|
|
108
|
+
if (sitemapName !== "index" && sitemapConfig._isChunking) {
|
|
109
|
+
const chunkSize = sitemapConfig._chunkSize || defaultSitemapsChunkSize || 1e3;
|
|
110
|
+
let sourcesInput = sitemapConfig.includeAppSources ? await globalSitemapSources() : [];
|
|
111
|
+
sourcesInput.push(...await childSitemapSources(sitemapConfig));
|
|
112
|
+
if (nitro && resolvers.event) {
|
|
113
|
+
const ctx = {
|
|
114
|
+
event: resolvers.event,
|
|
115
|
+
sitemapName: sitemapConfig.sitemapName,
|
|
116
|
+
sources: sourcesInput
|
|
117
|
+
};
|
|
118
|
+
await nitro.hooks.callHook("sitemap:sources", ctx);
|
|
119
|
+
sourcesInput = ctx.sources;
|
|
120
|
+
}
|
|
121
|
+
const sources = await resolveSitemapSources(sourcesInput, resolvers.event);
|
|
122
|
+
const failedSources = sources.filter((source) => source.error && source._isFailure).map((source) => ({
|
|
123
|
+
url: typeof source.fetch === "string" ? source.fetch : source.fetch?.[0] || "unknown",
|
|
124
|
+
error: source.error || "Unknown error"
|
|
125
|
+
}));
|
|
126
|
+
allFailedSources.push(...failedSources);
|
|
127
|
+
const resolvedCtx = {
|
|
128
|
+
urls: sources.flatMap((s) => s.urls),
|
|
129
|
+
sitemapName: sitemapConfig.sitemapName,
|
|
130
|
+
event: resolvers.event
|
|
131
|
+
};
|
|
132
|
+
await nitro?.hooks.callHook("sitemap:input", resolvedCtx);
|
|
133
|
+
const normalisedUrls = resolveSitemapEntries(sitemapConfig, resolvedCtx.urls, { autoI18n, isI18nMapped }, resolvers);
|
|
134
|
+
const totalUrls = normalisedUrls.length;
|
|
135
|
+
const chunkCount = Math.ceil(totalUrls / chunkSize);
|
|
136
|
+
sitemapConfig._chunkCount = chunkCount;
|
|
137
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
138
|
+
const chunkName = `${sitemapName}-${i}`;
|
|
139
|
+
const entry = {
|
|
140
|
+
_sitemapName: chunkName,
|
|
141
|
+
sitemap: resolvers.canonicalUrlResolver(joinURL(sitemapsPathPrefix || "", `/${chunkName}.xml`))
|
|
142
|
+
};
|
|
143
|
+
const chunkUrls = normalisedUrls.slice(i * chunkSize, (i + 1) * chunkSize);
|
|
144
|
+
let lastmod = chunkUrls.filter((a) => !!a?.lastmod).map((a) => typeof a.lastmod === "string" ? new Date(a.lastmod) : a.lastmod).sort((a, b) => (b?.getTime() || 0) - (a?.getTime() || 0))?.[0];
|
|
145
|
+
if (!lastmod && autoLastmod)
|
|
146
|
+
lastmod = /* @__PURE__ */ new Date();
|
|
147
|
+
if (lastmod)
|
|
148
|
+
entry.lastmod = normaliseDate(lastmod);
|
|
149
|
+
entries.push(entry);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (sitemaps.index) {
|
|
154
|
+
entries.push(...sitemaps.index.sitemaps.map((entry) => {
|
|
155
|
+
return typeof entry === "string" ? { sitemap: entry } : entry;
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
return { entries, failedSources: allFailedSources };
|
|
159
|
+
}
|
|
160
|
+
export function urlsToIndexXml(sitemaps, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
161
|
+
const sitemapXml = sitemaps.map((e) => [
|
|
162
|
+
" <sitemap>",
|
|
163
|
+
` <loc>${escapeValueForXml(e.sitemap)}</loc>`,
|
|
164
|
+
// lastmod is optional
|
|
165
|
+
e.lastmod ? ` <lastmod>${escapeValueForXml(e.lastmod)}</lastmod>` : false,
|
|
166
|
+
" </sitemap>"
|
|
167
|
+
].filter(Boolean).join("\n")).join("\n");
|
|
168
|
+
const xmlParts = [
|
|
169
|
+
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
170
|
+
];
|
|
171
|
+
if (xsl) {
|
|
172
|
+
let relativeBaseUrl = resolvers.relativeBaseUrlResolver?.(xsl) ?? xsl;
|
|
173
|
+
if (errorInfo && errorInfo.messages.length > 0) {
|
|
174
|
+
relativeBaseUrl = withQuery(relativeBaseUrl, {
|
|
175
|
+
errors: "true",
|
|
176
|
+
error_messages: errorInfo.messages,
|
|
177
|
+
error_urls: errorInfo.urls
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
xmlParts.push(`<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`);
|
|
181
|
+
}
|
|
182
|
+
xmlParts.push(
|
|
183
|
+
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
184
|
+
sitemapXml,
|
|
185
|
+
"</sitemapindex>"
|
|
186
|
+
);
|
|
187
|
+
if (credits) {
|
|
188
|
+
xmlParts.push(`<!-- XML Sitemap Index generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`);
|
|
189
|
+
}
|
|
190
|
+
return minify ? xmlParts.join("").replace(/(?<!<[^>]*)\s(?![^<]*>)/g, "") : xmlParts.join("\n");
|
|
191
|
+
}
|
|
192
|
+
export async function buildSitemapIndex(resolvers, runtimeConfig, nitro) {
|
|
193
|
+
if (!import.meta.dev && typeof runtimeConfig.cacheMaxAgeSeconds === "number" && runtimeConfig.cacheMaxAgeSeconds > 0 && resolvers.event) {
|
|
194
|
+
return buildSitemapIndexCached(resolvers.event, resolvers, runtimeConfig, nitro);
|
|
195
|
+
}
|
|
196
|
+
return buildSitemapIndexInternal(resolvers, runtimeConfig, nitro);
|
|
197
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NitroApp } from 'nitropack/types';
|
|
2
|
+
import type { AutoI18nConfig, ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl, SitemapDefinition, SitemapUrlInput } from '../../../types.js';
|
|
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): ResolvedSitemapUrl[];
|
|
9
|
+
export declare function buildSitemapUrls(sitemap: SitemapDefinition, resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<{
|
|
10
|
+
urls: ResolvedSitemapUrl[];
|
|
11
|
+
failedSources: Array<{
|
|
12
|
+
url: string;
|
|
13
|
+
error: string;
|
|
14
|
+
}>;
|
|
15
|
+
}>;
|
|
16
|
+
export { urlsToXml } from './xml.js';
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { resolveSitePath } from "nuxt-site-config/urls";
|
|
2
|
+
import { joinURL, withHttps } from "ufo";
|
|
3
|
+
import { preNormalizeEntry } from "../urlset/normalise.js";
|
|
4
|
+
import { childSitemapSources, globalSitemapSources, resolveSitemapSources } from "../urlset/sources.js";
|
|
5
|
+
import { sortInPlace } from "../urlset/sort.js";
|
|
6
|
+
import { createPathFilter, logger, splitForLocales } from "../../../utils-pure.js";
|
|
7
|
+
import { parseChunkInfo, sliceUrlsForChunk } from "../utils/chunk.js";
|
|
8
|
+
export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers) {
|
|
9
|
+
const {
|
|
10
|
+
autoI18n,
|
|
11
|
+
isI18nMapped
|
|
12
|
+
} = runtimeConfig;
|
|
13
|
+
const filterPath = createPathFilter({
|
|
14
|
+
include: sitemap.include,
|
|
15
|
+
exclude: sitemap.exclude
|
|
16
|
+
});
|
|
17
|
+
const _urls = urls.map((_e) => {
|
|
18
|
+
const e = preNormalizeEntry(_e, resolvers);
|
|
19
|
+
if (!e.loc || !filterPath(e.loc))
|
|
20
|
+
return false;
|
|
21
|
+
return e;
|
|
22
|
+
}).filter(Boolean);
|
|
23
|
+
let validI18nUrlsForTransform = [];
|
|
24
|
+
let warnIncorrectI18nTransformUsage = false;
|
|
25
|
+
const withoutPrefixPaths = {};
|
|
26
|
+
if (autoI18n && autoI18n.strategy !== "no_prefix") {
|
|
27
|
+
const localeCodes = autoI18n.locales.map((l) => l.code);
|
|
28
|
+
validI18nUrlsForTransform = _urls.map((_e, i) => {
|
|
29
|
+
if (_e._abs)
|
|
30
|
+
return false;
|
|
31
|
+
const split = splitForLocales(_e._relativeLoc, localeCodes);
|
|
32
|
+
let localeCode = split[0];
|
|
33
|
+
const pathWithoutPrefix = split[1];
|
|
34
|
+
if (!localeCode)
|
|
35
|
+
localeCode = autoI18n.defaultLocale;
|
|
36
|
+
const e = _e;
|
|
37
|
+
e._pathWithoutPrefix = pathWithoutPrefix;
|
|
38
|
+
const locale = autoI18n.locales.find((l) => l.code === localeCode);
|
|
39
|
+
if (!locale)
|
|
40
|
+
return false;
|
|
41
|
+
e._locale = locale;
|
|
42
|
+
e._index = i;
|
|
43
|
+
e._key = `${e._sitemap || ""}${e._path?.pathname || "/"}${e._path.search}`;
|
|
44
|
+
withoutPrefixPaths[pathWithoutPrefix] = withoutPrefixPaths[pathWithoutPrefix] || [];
|
|
45
|
+
if (!withoutPrefixPaths[pathWithoutPrefix].some((e2) => e2._locale.code === locale.code))
|
|
46
|
+
withoutPrefixPaths[pathWithoutPrefix].push(e);
|
|
47
|
+
return e;
|
|
48
|
+
}).filter(Boolean);
|
|
49
|
+
for (const e of validI18nUrlsForTransform) {
|
|
50
|
+
if (!e._i18nTransform && !e.alternatives?.length) {
|
|
51
|
+
const alternatives = withoutPrefixPaths[e._pathWithoutPrefix].map((u) => {
|
|
52
|
+
const entries = [];
|
|
53
|
+
if (u._locale.code === autoI18n.defaultLocale) {
|
|
54
|
+
entries.push({
|
|
55
|
+
href: u.loc,
|
|
56
|
+
hreflang: "x-default"
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
entries.push({
|
|
60
|
+
href: u.loc,
|
|
61
|
+
hreflang: u._locale._hreflang || autoI18n.defaultLocale
|
|
62
|
+
});
|
|
63
|
+
return entries;
|
|
64
|
+
}).flat().filter(Boolean);
|
|
65
|
+
if (alternatives.length)
|
|
66
|
+
e.alternatives = alternatives;
|
|
67
|
+
} else if (e._i18nTransform) {
|
|
68
|
+
delete e._i18nTransform;
|
|
69
|
+
if (autoI18n.strategy === "no_prefix") {
|
|
70
|
+
warnIncorrectI18nTransformUsage = true;
|
|
71
|
+
}
|
|
72
|
+
if (autoI18n.differentDomains) {
|
|
73
|
+
e.alternatives = [
|
|
74
|
+
{
|
|
75
|
+
// apply default locale domain
|
|
76
|
+
...autoI18n.locales.find((l) => [l.code, l.language].includes(autoI18n.defaultLocale)),
|
|
77
|
+
code: "x-default"
|
|
78
|
+
},
|
|
79
|
+
...autoI18n.locales.filter((l) => !!l.domain)
|
|
80
|
+
].map((locale) => {
|
|
81
|
+
return {
|
|
82
|
+
hreflang: locale._hreflang,
|
|
83
|
+
href: joinURL(withHttps(locale.domain), e._pathWithoutPrefix)
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
for (const l of autoI18n.locales) {
|
|
88
|
+
let loc = e._pathWithoutPrefix;
|
|
89
|
+
if (autoI18n.pages) {
|
|
90
|
+
const pageKey = e._pathWithoutPrefix.replace(/^\//, "").replace(/\/index$/, "") || "index";
|
|
91
|
+
const pageMappings = autoI18n.pages[pageKey];
|
|
92
|
+
if (pageMappings && pageMappings[l.code] !== void 0) {
|
|
93
|
+
const customPath = pageMappings[l.code];
|
|
94
|
+
if (customPath === false)
|
|
95
|
+
continue;
|
|
96
|
+
if (typeof customPath === "string")
|
|
97
|
+
loc = customPath.startsWith("/") ? customPath : `/${customPath}`;
|
|
98
|
+
} else if (!autoI18n.differentDomains && !(["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy) && l.code === autoI18n.defaultLocale)) {
|
|
99
|
+
loc = joinURL(`/${l.code}`, e._pathWithoutPrefix);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
if (!autoI18n.differentDomains && !(["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy) && l.code === autoI18n.defaultLocale))
|
|
103
|
+
loc = joinURL(`/${l.code}`, e._pathWithoutPrefix);
|
|
104
|
+
}
|
|
105
|
+
const _sitemap = isI18nMapped ? l._sitemap : void 0;
|
|
106
|
+
const newEntry = preNormalizeEntry({
|
|
107
|
+
_sitemap,
|
|
108
|
+
...e,
|
|
109
|
+
_index: void 0,
|
|
110
|
+
_key: `${_sitemap || ""}${loc || "/"}${e._path.search}`,
|
|
111
|
+
_locale: l,
|
|
112
|
+
loc,
|
|
113
|
+
alternatives: [{ code: "x-default", _hreflang: "x-default" }, ...autoI18n.locales].map((locale) => {
|
|
114
|
+
const code = locale.code === "x-default" ? autoI18n.defaultLocale : locale.code;
|
|
115
|
+
const isDefault = locale.code === "x-default" || locale.code === autoI18n.defaultLocale;
|
|
116
|
+
let href = e._pathWithoutPrefix;
|
|
117
|
+
if (autoI18n.pages) {
|
|
118
|
+
const pageKey = e._pathWithoutPrefix.replace(/^\//, "").replace(/\/index$/, "") || "index";
|
|
119
|
+
const pageMappings = autoI18n.pages[pageKey];
|
|
120
|
+
if (pageMappings && pageMappings[code] !== void 0) {
|
|
121
|
+
const customPath = pageMappings[code];
|
|
122
|
+
if (customPath === false)
|
|
123
|
+
return false;
|
|
124
|
+
if (typeof customPath === "string")
|
|
125
|
+
href = customPath.startsWith("/") ? customPath : `/${customPath}`;
|
|
126
|
+
} else if (autoI18n.strategy === "prefix") {
|
|
127
|
+
href = joinURL("/", code, e._pathWithoutPrefix);
|
|
128
|
+
} else if (["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy)) {
|
|
129
|
+
if (!isDefault) {
|
|
130
|
+
href = joinURL("/", code, e._pathWithoutPrefix);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
if (autoI18n.strategy === "prefix") {
|
|
135
|
+
href = joinURL("/", code, e._pathWithoutPrefix);
|
|
136
|
+
} else if (["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy)) {
|
|
137
|
+
if (!isDefault) {
|
|
138
|
+
href = joinURL("/", code, e._pathWithoutPrefix);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (!filterPath(href))
|
|
143
|
+
return false;
|
|
144
|
+
return {
|
|
145
|
+
hreflang: locale._hreflang,
|
|
146
|
+
href
|
|
147
|
+
};
|
|
148
|
+
}).filter(Boolean)
|
|
149
|
+
}, resolvers);
|
|
150
|
+
if (e._locale.code === newEntry._locale.code) {
|
|
151
|
+
_urls[e._index] = newEntry;
|
|
152
|
+
e._index = void 0;
|
|
153
|
+
} else {
|
|
154
|
+
_urls.push(newEntry);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (isI18nMapped) {
|
|
160
|
+
e._sitemap = e._sitemap || e._locale._sitemap;
|
|
161
|
+
e._key = `${e._sitemap || ""}${e.loc || "/"}${e._path.search}`;
|
|
162
|
+
}
|
|
163
|
+
if (e._index)
|
|
164
|
+
_urls[e._index] = e;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (import.meta.dev && warnIncorrectI18nTransformUsage) {
|
|
168
|
+
logger.warn("You're using _i18nTransform with the `no_prefix` strategy. This will cause issues with the sitemap. Please remove the _i18nTransform flag or change i18n strategy.");
|
|
169
|
+
}
|
|
170
|
+
return _urls;
|
|
171
|
+
}
|
|
172
|
+
export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig, nitro) {
|
|
173
|
+
const {
|
|
174
|
+
sitemaps,
|
|
175
|
+
// enhancing
|
|
176
|
+
autoI18n,
|
|
177
|
+
isI18nMapped,
|
|
178
|
+
isMultiSitemap,
|
|
179
|
+
// sorting
|
|
180
|
+
sortEntries,
|
|
181
|
+
// chunking
|
|
182
|
+
defaultSitemapsChunkSize
|
|
183
|
+
} = runtimeConfig;
|
|
184
|
+
const chunkInfo = parseChunkInfo(sitemap.sitemapName, sitemaps, defaultSitemapsChunkSize);
|
|
185
|
+
function maybeSort(urls2) {
|
|
186
|
+
return sortEntries ? sortInPlace(urls2) : urls2;
|
|
187
|
+
}
|
|
188
|
+
function maybeSlice(urls2) {
|
|
189
|
+
return sliceUrlsForChunk(urls2, sitemap.sitemapName, sitemaps, defaultSitemapsChunkSize);
|
|
190
|
+
}
|
|
191
|
+
if (autoI18n?.differentDomains) {
|
|
192
|
+
const domain = autoI18n.locales.find((e) => [e.language, e.code].includes(sitemap.sitemapName))?.domain;
|
|
193
|
+
if (domain) {
|
|
194
|
+
const _tester = resolvers.canonicalUrlResolver;
|
|
195
|
+
resolvers.canonicalUrlResolver = (path) => resolveSitePath(path, {
|
|
196
|
+
absolute: true,
|
|
197
|
+
withBase: false,
|
|
198
|
+
siteUrl: withHttps(domain),
|
|
199
|
+
trailingSlash: _tester("/test/").endsWith("/"),
|
|
200
|
+
base: "/"
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
let effectiveSitemap = sitemap;
|
|
205
|
+
const baseSitemapName = chunkInfo.baseSitemapName;
|
|
206
|
+
if (chunkInfo.isChunked && baseSitemapName !== sitemap.sitemapName && sitemaps[baseSitemapName]) {
|
|
207
|
+
effectiveSitemap = sitemaps[baseSitemapName];
|
|
208
|
+
}
|
|
209
|
+
let sourcesInput = effectiveSitemap.includeAppSources ? await globalSitemapSources() : [];
|
|
210
|
+
sourcesInput.push(...await childSitemapSources(effectiveSitemap));
|
|
211
|
+
if (nitro && resolvers.event) {
|
|
212
|
+
const ctx = {
|
|
213
|
+
event: resolvers.event,
|
|
214
|
+
sitemapName: baseSitemapName,
|
|
215
|
+
sources: sourcesInput
|
|
216
|
+
};
|
|
217
|
+
await nitro.hooks.callHook("sitemap:sources", ctx);
|
|
218
|
+
sourcesInput = ctx.sources;
|
|
219
|
+
}
|
|
220
|
+
const sources = await resolveSitemapSources(sourcesInput, resolvers.event);
|
|
221
|
+
const failedSources = sources.filter((source) => source.error && source._isFailure).map((source) => ({
|
|
222
|
+
url: typeof source.fetch === "string" ? source.fetch : source.fetch?.[0] || "unknown",
|
|
223
|
+
error: source.error || "Unknown error"
|
|
224
|
+
}));
|
|
225
|
+
const resolvedCtx = {
|
|
226
|
+
urls: sources.flatMap((s) => s.urls),
|
|
227
|
+
sitemapName: sitemap.sitemapName,
|
|
228
|
+
event: resolvers.event
|
|
229
|
+
};
|
|
230
|
+
await nitro?.hooks.callHook("sitemap:input", resolvedCtx);
|
|
231
|
+
const enhancedUrls = resolveSitemapEntries(sitemap, resolvedCtx.urls, { autoI18n, isI18nMapped }, resolvers);
|
|
232
|
+
const filteredUrls = enhancedUrls.filter((e) => {
|
|
233
|
+
if (isMultiSitemap && e._sitemap && sitemap.sitemapName)
|
|
234
|
+
return e._sitemap === sitemap.sitemapName;
|
|
235
|
+
return true;
|
|
236
|
+
});
|
|
237
|
+
const sortedUrls = maybeSort(filteredUrls);
|
|
238
|
+
const urls = maybeSlice(sortedUrls);
|
|
239
|
+
return { urls, failedSources };
|
|
240
|
+
}
|
|
241
|
+
export { urlsToXml } from "./xml.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl } from '../../../types.js';
|
|
2
|
+
export declare function escapeValueForXml(value: boolean | string | number): string;
|
|
3
|
+
export declare function urlsToXml(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
4
|
+
messages: string[];
|
|
5
|
+
urls: string[];
|
|
6
|
+
}): string;
|