@nuxtjs/sitemap 8.2.3 → 8.3.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.
- package/dist/module.json +1 -1
- package/dist/module.mjs +8 -3
- package/dist/runtime/server/kit.js +9 -2
- package/dist/runtime/server/plugins/compression.js +57 -13
- package/dist/runtime/server/plugins/stream-transport.d.ts +2 -0
- package/dist/runtime/server/plugins/stream-transport.js +16 -0
- package/dist/runtime/server/routes/__sitemap__/debug.d.ts +1 -0
- package/dist/runtime/server/routes/__zero-runtime/sitemap/[sitemap].xml.d.ts +1 -1
- package/dist/runtime/server/routes/__zero-runtime/sitemap.xml.d.ts +1 -1
- package/dist/runtime/server/routes/__zero-runtime/sitemap_index.xml.d.ts +1 -1
- package/dist/runtime/server/routes/sitemap/[sitemap].xml.d.ts +1 -1
- package/dist/runtime/server/routes/sitemap.xml.d.ts +1 -1
- package/dist/runtime/server/routes/sitemap_index.xml.d.ts +1 -1
- package/dist/runtime/server/sitemap/builder/index-xml.d.ts +13 -0
- package/dist/runtime/server/sitemap/builder/index-xml.js +70 -0
- package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +1 -4
- package/dist/runtime/server/sitemap/builder/sitemap-index.js +2 -34
- package/dist/runtime/server/sitemap/builder/sitemap.d.ts +1 -1
- package/dist/runtime/server/sitemap/builder/sitemap.js +36 -25
- package/dist/runtime/server/sitemap/builder/xml.d.ts +8 -0
- package/dist/runtime/server/sitemap/builder/xml.js +35 -6
- package/dist/runtime/server/sitemap/event-handlers.d.ts +3 -3
- package/dist/runtime/server/sitemap/event-handlers.js +18 -10
- package/dist/runtime/server/sitemap/nitro.d.ts +3 -1
- package/dist/runtime/server/sitemap/nitro.js +107 -20
- package/dist/runtime/server/sitemap/stream.d.ts +30 -0
- package/dist/runtime/server/sitemap/stream.js +144 -0
- package/dist/runtime/server/sitemap/urlset/normalise.d.ts +6 -1
- package/dist/runtime/server/sitemap/urlset/normalise.js +15 -4
- package/dist/runtime/server/sitemap/urlset/sort.js +12 -3
- package/dist/runtime/server/sitemap/urlset/sources.js +3 -3
- package/dist/runtime/server/utils.d.ts +0 -1
- package/dist/runtime/server/utils.js +12 -12
- package/dist/runtime/types.d.ts +14 -3
- package/dist/runtime/utils-pure.d.ts +5 -4
- package/dist/runtime/utils-pure.js +41 -19
- package/dist/utils.d.mts +30 -2
- package/dist/utils.d.ts +30 -2
- package/dist/utils.mjs +646 -43
- package/package.json +8 -8
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -53,6 +53,7 @@ function setupPrerenderHandler(_options, nuxt = useNuxt()) {
|
|
|
53
53
|
}
|
|
54
54
|
nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes.filter((r) => r && !includesSitemapRoot(options.sitemapName, [r]));
|
|
55
55
|
const runtimeAssetsPath = join(nuxt.options.rootDir, "node_modules/.cache/nuxt/sitemap");
|
|
56
|
+
const localeCodes = options.autoI18n ? new Set(options.autoI18n.locales.map((l) => l.code)) : void 0;
|
|
56
57
|
nuxt.hooks.hook("nitro:config", (nitroConfig) => {
|
|
57
58
|
nitroConfig.virtual = nitroConfig.virtual || {};
|
|
58
59
|
nitroConfig.virtual["#sitemap-virtual/read-sources.mjs"] = `
|
|
@@ -103,7 +104,7 @@ export async function readSourcesFromFilesystem(filename) {
|
|
|
103
104
|
});
|
|
104
105
|
if (options.autoI18n && Object.keys(options.sitemaps).length > 1) {
|
|
105
106
|
const path = route.route;
|
|
106
|
-
const match = splitForLocales(path,
|
|
107
|
+
const match = splitForLocales(path, localeCodes);
|
|
107
108
|
const locale = match[0] || options.autoI18n.defaultLocale;
|
|
108
109
|
if (options.isI18nMapped) {
|
|
109
110
|
const { _sitemap } = options.autoI18n.locales.find((l) => l.code === locale) || { _sitemap: locale };
|
|
@@ -501,6 +502,7 @@ const module$1 = defineNuxtModule({
|
|
|
501
502
|
// cache for 10 minutes
|
|
502
503
|
minify: false,
|
|
503
504
|
debug: false,
|
|
505
|
+
experimentalStreaming: false,
|
|
504
506
|
defaultSitemapsChunkSize: 1e3,
|
|
505
507
|
autoLastmod: false,
|
|
506
508
|
discoverImages: true,
|
|
@@ -767,13 +769,15 @@ const module$1 = defineNuxtModule({
|
|
|
767
769
|
} else if (hasRouteRuleContent) {
|
|
768
770
|
nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
|
|
769
771
|
}
|
|
770
|
-
if (config.zeroRuntime && (config.experimentalWarmUp || config.experimentalCompression))
|
|
771
|
-
logger.warn("`experimentalWarmUp` and `
|
|
772
|
+
if (config.zeroRuntime && (config.experimentalWarmUp || config.experimentalCompression || config.experimentalStreaming))
|
|
773
|
+
logger.warn("`experimentalWarmUp`, `experimentalCompression`, and `experimentalStreaming` are ignored in zeroRuntime mode.");
|
|
772
774
|
if (!config.zeroRuntime) {
|
|
773
775
|
if (config.experimentalWarmUp)
|
|
774
776
|
addServerPlugin(resolve("./runtime/server/plugins/warm-up"));
|
|
775
777
|
if (config.experimentalCompression)
|
|
776
778
|
addServerPlugin(resolve("./runtime/server/plugins/compression"));
|
|
779
|
+
if (config.experimentalStreaming || config.experimentalCompression)
|
|
780
|
+
addServerPlugin(resolve("./runtime/server/plugins/stream-transport"));
|
|
777
781
|
}
|
|
778
782
|
const isNuxtContentDocumentDriven = !!nuxt.options.content?.documentDriven || config.strictNuxtContentPaths;
|
|
779
783
|
const contentVersion = await resolveNuxtContentVersion();
|
|
@@ -1051,6 +1055,7 @@ ${onUrlEntries.join("\n")}`;
|
|
|
1051
1055
|
isMultiSitemap: usingMultiSitemaps,
|
|
1052
1056
|
excludeAppSources: config.excludeAppSources,
|
|
1053
1057
|
cacheMaxAgeSeconds: nuxt.options.dev ? 0 : config.cacheMaxAgeSeconds,
|
|
1058
|
+
experimentalStreaming: config.experimentalStreaming,
|
|
1054
1059
|
autoLastmod: config.autoLastmod,
|
|
1055
1060
|
defaultSitemapsChunkSize: config.defaultSitemapsChunkSize,
|
|
1056
1061
|
minify: config.minify,
|
|
@@ -3,9 +3,13 @@ import { useRuntimeConfig } from "nitropack/runtime";
|
|
|
3
3
|
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
|
4
4
|
import { parseURL, withoutBase, withoutTrailingSlash } from "ufo";
|
|
5
5
|
function withoutQuery(path) {
|
|
6
|
-
|
|
6
|
+
const queryIndex = path.indexOf("?");
|
|
7
|
+
return queryIndex === -1 ? path : path.slice(0, queryIndex);
|
|
7
8
|
}
|
|
9
|
+
let cachedRouteRuleMatcher;
|
|
8
10
|
export function createNitroRouteRuleMatcher() {
|
|
11
|
+
if (!import.meta.dev && cachedRouteRuleMatcher)
|
|
12
|
+
return cachedRouteRuleMatcher;
|
|
9
13
|
const { nitro, app } = useRuntimeConfig();
|
|
10
14
|
const _routeRulesMatcher = toRouteMatcher(
|
|
11
15
|
createRadixRouter({
|
|
@@ -14,10 +18,13 @@ export function createNitroRouteRuleMatcher() {
|
|
|
14
18
|
)
|
|
15
19
|
})
|
|
16
20
|
);
|
|
17
|
-
|
|
21
|
+
const matcher = (pathOrUrl) => {
|
|
18
22
|
const path = pathOrUrl[0] === "/" ? pathOrUrl : parseURL(pathOrUrl, app.baseURL).pathname;
|
|
19
23
|
return defu({}, ..._routeRulesMatcher.matchAll(
|
|
20
24
|
withoutBase(withoutTrailingSlash(withoutQuery(path)), app.baseURL)
|
|
21
25
|
).reverse());
|
|
22
26
|
};
|
|
27
|
+
if (!import.meta.dev)
|
|
28
|
+
cachedRouteRuleMatcher = matcher;
|
|
29
|
+
return matcher;
|
|
23
30
|
}
|
|
@@ -1,23 +1,67 @@
|
|
|
1
|
-
import { getRequestHeader, setResponseHeader } from "h3";
|
|
1
|
+
import { appendResponseHeader, getRequestHeader, getResponseHeader, removeResponseHeader, setResponseHeader } from "h3";
|
|
2
2
|
import { defineNitroPlugin } from "nitropack/runtime";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import { logger } from "../../utils-pure.js";
|
|
4
|
+
import { hasNonIdentityEncoding, isReadableStream, negotiateCompressionEncoding } from "../sitemap/stream.js";
|
|
5
|
+
let warnedAboutCompressionStream = false;
|
|
6
|
+
const NODE_COMPRESSION_INPUT_BATCH_BYTES = 512 * 1024;
|
|
7
|
+
function toByteStream(body) {
|
|
8
|
+
if (isReadableStream(body))
|
|
9
|
+
return body;
|
|
10
|
+
if (body instanceof Blob)
|
|
11
|
+
return body.stream();
|
|
12
|
+
const value = typeof body === "string" || body instanceof ArrayBuffer || ArrayBuffer.isView(body) ? body : JSON.stringify(body);
|
|
13
|
+
return new Blob([value]).stream();
|
|
14
|
+
}
|
|
15
|
+
function createNodeCompressionInputStream(source) {
|
|
16
|
+
const reader = source.getReader();
|
|
17
|
+
let bytesRead = 0;
|
|
18
|
+
return new ReadableStream({
|
|
19
|
+
async pull(controller) {
|
|
20
|
+
if (bytesRead >= NODE_COMPRESSION_INPUT_BATCH_BYTES) {
|
|
21
|
+
bytesRead = 0;
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
23
|
+
}
|
|
24
|
+
const result = await reader.read();
|
|
25
|
+
if (result.done) {
|
|
26
|
+
controller.close();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
bytesRead += result.value.byteLength;
|
|
30
|
+
controller.enqueue(result.value);
|
|
31
|
+
},
|
|
32
|
+
cancel(reason) {
|
|
33
|
+
return reader.cancel(reason);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function addVaryAcceptEncoding(event) {
|
|
38
|
+
const vary = getResponseHeader(event, "Vary");
|
|
39
|
+
const values = (Array.isArray(vary) ? vary : [vary]).flatMap((value) => String(value || "").split(",")).map((value) => value.trim().toLowerCase());
|
|
40
|
+
if (!values.includes("*") && !values.includes("accept-encoding"))
|
|
41
|
+
appendResponseHeader(event, "Vary", "Accept-Encoding");
|
|
10
42
|
}
|
|
11
43
|
export default defineNitroPlugin((nitro) => {
|
|
12
|
-
nitro.hooks.hook("beforeResponse",
|
|
44
|
+
nitro.hooks.hook("beforeResponse", (event, response) => {
|
|
13
45
|
if (!event.context._isSitemap || !response.body)
|
|
14
46
|
return;
|
|
15
|
-
|
|
47
|
+
addVaryAcceptEncoding(event);
|
|
48
|
+
if (hasNonIdentityEncoding(getResponseHeader(event, "Content-Encoding")))
|
|
49
|
+
return;
|
|
50
|
+
const encoding = negotiateCompressionEncoding(getRequestHeader(event, "accept-encoding") || "");
|
|
16
51
|
if (!encoding)
|
|
17
52
|
return;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
53
|
+
if (typeof CompressionStream === "undefined") {
|
|
54
|
+
if (!warnedAboutCompressionStream) {
|
|
55
|
+
warnedAboutCompressionStream = true;
|
|
56
|
+
logger.warn("Sitemap compression was requested, but CompressionStream is unavailable in this runtime. Sending the uncompressed response.");
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const compression = new CompressionStream(encoding);
|
|
61
|
+
const source = toByteStream(response.body);
|
|
62
|
+
const compressionInput = event.node.res?.socket ? createNodeCompressionInputStream(source) : source;
|
|
63
|
+
response.body = compressionInput.pipeThrough(compression);
|
|
64
|
+
removeResponseHeader(event, "Content-Length");
|
|
21
65
|
setResponseHeader(event, "Content-Encoding", encoding);
|
|
22
66
|
});
|
|
23
67
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { removeResponseHeader } from "h3";
|
|
2
|
+
import { defineNitroPlugin } from "nitropack/runtime";
|
|
3
|
+
import { logger } from "../../utils-pure.js";
|
|
4
|
+
import { createNodeResponseStream, isReadableStream } from "../sitemap/stream.js";
|
|
5
|
+
export default defineNitroPlugin((nitro) => {
|
|
6
|
+
nitro.hooks.hook("beforeResponse", (event, response) => {
|
|
7
|
+
const nodeResponse = event.node.res;
|
|
8
|
+
if (!event.context._isSitemap || !isReadableStream(response.body) || !nodeResponse?.socket || typeof nodeResponse?.write !== "function" || typeof nodeResponse?.once !== "function") {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
response.body = createNodeResponseStream(response.body, (error) => {
|
|
12
|
+
logger.error("Failed to cancel sitemap response stream after the client disconnected.", error);
|
|
13
|
+
});
|
|
14
|
+
removeResponseHeader(event, "Content-Length");
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | undefined>>;
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | ReadableStream<Uint8Array<ArrayBufferLike>> | undefined>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | void
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | void | ReadableStream<Uint8Array<ArrayBufferLike>>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | ReadableStream<Uint8Array<ArrayBufferLike>>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | undefined>>;
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | ReadableStream<Uint8Array<ArrayBufferLike>> | undefined>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | void
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | void | ReadableStream<Uint8Array<ArrayBufferLike>>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | ReadableStream<Uint8Array<ArrayBufferLike>>>>;
|
|
2
2
|
export default _default;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapIndexEntry } from '../../../types.js';
|
|
2
|
+
export declare function renderSitemapIndexXmlChunks(sitemaps: SitemapIndexEntry[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
3
|
+
messages: string[];
|
|
4
|
+
urls: string[];
|
|
5
|
+
}): Generator<string>;
|
|
6
|
+
export declare function urlsToIndexXml(sitemaps: SitemapIndexEntry[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
7
|
+
messages: string[];
|
|
8
|
+
urls: string[];
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function urlsToIndexXmlStream(sitemaps: SitemapIndexEntry[], resolvers: NitroUrlResolvers, config: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
11
|
+
messages: string[];
|
|
12
|
+
urls: string[];
|
|
13
|
+
}): ReadableStream<Uint8Array>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { withQuery } from "ufo";
|
|
2
|
+
import { createChunkedXmlStream } from "../stream.js";
|
|
3
|
+
import { escapeValueForXml } from "./xml.js";
|
|
4
|
+
export function* renderSitemapIndexXmlChunks(sitemaps, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
5
|
+
const NL = minify ? "" : "\n";
|
|
6
|
+
const I1 = minify ? "" : " ";
|
|
7
|
+
const I2 = minify ? "" : " ";
|
|
8
|
+
yield '<?xml version="1.0" encoding="UTF-8"?>';
|
|
9
|
+
if (xsl) {
|
|
10
|
+
let relativeBaseUrl = resolvers.relativeBaseUrlResolver?.(xsl) ?? xsl;
|
|
11
|
+
if (errorInfo && errorInfo.messages.length > 0) {
|
|
12
|
+
relativeBaseUrl = withQuery(relativeBaseUrl, {
|
|
13
|
+
errors: "true",
|
|
14
|
+
error_messages: errorInfo.messages,
|
|
15
|
+
error_urls: errorInfo.urls
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
yield `${NL}<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`;
|
|
19
|
+
}
|
|
20
|
+
yield `${NL}<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${NL}`;
|
|
21
|
+
for (let i = 0; i < sitemaps.length; i++) {
|
|
22
|
+
const entry = sitemaps[i];
|
|
23
|
+
if (i > 0)
|
|
24
|
+
yield NL;
|
|
25
|
+
yield `${I1}<sitemap>${NL}${I2}<loc>${escapeValueForXml(entry.sitemap)}</loc>${NL}`;
|
|
26
|
+
if (entry.lastmod)
|
|
27
|
+
yield `${I2}<lastmod>${escapeValueForXml(entry.lastmod)}</lastmod>${NL}`;
|
|
28
|
+
yield `${I1}</sitemap>`;
|
|
29
|
+
}
|
|
30
|
+
yield `${NL}</sitemapindex>`;
|
|
31
|
+
if (credits)
|
|
32
|
+
yield `${NL}<!-- XML Sitemap Index generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`;
|
|
33
|
+
}
|
|
34
|
+
export function urlsToIndexXml(sitemaps, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
35
|
+
const NL = minify ? "" : "\n";
|
|
36
|
+
const I1 = minify ? "" : " ";
|
|
37
|
+
const I2 = minify ? "" : " ";
|
|
38
|
+
let sitemapXml = "";
|
|
39
|
+
for (const entry of sitemaps) {
|
|
40
|
+
if (sitemapXml)
|
|
41
|
+
sitemapXml += NL;
|
|
42
|
+
sitemapXml += `${I1}<sitemap>${NL}${I2}<loc>${escapeValueForXml(entry.sitemap)}</loc>${NL}`;
|
|
43
|
+
if (entry.lastmod)
|
|
44
|
+
sitemapXml += `${I2}<lastmod>${escapeValueForXml(entry.lastmod)}</lastmod>${NL}`;
|
|
45
|
+
sitemapXml += `${I1}</sitemap>`;
|
|
46
|
+
}
|
|
47
|
+
const xmlParts = ['<?xml version="1.0" encoding="UTF-8"?>'];
|
|
48
|
+
if (xsl) {
|
|
49
|
+
let relativeBaseUrl = resolvers.relativeBaseUrlResolver?.(xsl) ?? xsl;
|
|
50
|
+
if (errorInfo && errorInfo.messages.length > 0) {
|
|
51
|
+
relativeBaseUrl = withQuery(relativeBaseUrl, {
|
|
52
|
+
errors: "true",
|
|
53
|
+
error_messages: errorInfo.messages,
|
|
54
|
+
error_urls: errorInfo.urls
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
xmlParts.push(`<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`);
|
|
58
|
+
}
|
|
59
|
+
xmlParts.push(
|
|
60
|
+
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
61
|
+
sitemapXml,
|
|
62
|
+
"</sitemapindex>"
|
|
63
|
+
);
|
|
64
|
+
if (credits)
|
|
65
|
+
xmlParts.push(`<!-- XML Sitemap Index generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`);
|
|
66
|
+
return xmlParts.join(NL);
|
|
67
|
+
}
|
|
68
|
+
export function urlsToIndexXmlStream(sitemaps, resolvers, config, errorInfo) {
|
|
69
|
+
return createChunkedXmlStream(renderSitemapIndexXmlChunks(sitemaps, resolvers, config, errorInfo));
|
|
70
|
+
}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { NitroApp } from 'nitropack/types';
|
|
2
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
3
|
export declare function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<{
|
|
8
4
|
entries: SitemapIndexEntry[];
|
|
9
5
|
failedSources: Array<{
|
|
@@ -11,3 +7,4 @@ export declare function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeC
|
|
|
11
7
|
error: string;
|
|
12
8
|
}>;
|
|
13
9
|
}>;
|
|
10
|
+
export { urlsToIndexXml, urlsToIndexXmlStream } from './index-xml.js';
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { getHeader } from "h3";
|
|
2
2
|
import { defineCachedFunction } from "nitropack/runtime";
|
|
3
|
-
import { joinURL
|
|
3
|
+
import { joinURL } from "ufo";
|
|
4
4
|
import staticConfig from "#sitemap-virtual/static-config.mjs";
|
|
5
5
|
import { normaliseDate } from "../urlset/normalise.js";
|
|
6
6
|
import { getResolvedSitemapUrls } from "./sitemap.js";
|
|
7
|
-
import { escapeValueForXml } from "./xml.js";
|
|
8
7
|
const SERVER_CACHE_MAX_AGE = staticConfig.cacheMaxAgeSeconds || 60 * 10;
|
|
9
8
|
const buildSitemapIndexCached = defineCachedFunction(
|
|
10
9
|
async (event, resolvers, runtimeConfig, nitro) => {
|
|
@@ -105,41 +104,10 @@ async function buildSitemapIndexInternal(resolvers, runtimeConfig, nitro) {
|
|
|
105
104
|
}
|
|
106
105
|
return { entries, failedSources: allFailedSources };
|
|
107
106
|
}
|
|
108
|
-
export function urlsToIndexXml(sitemaps, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
109
|
-
const sitemapXml = sitemaps.map((e) => [
|
|
110
|
-
" <sitemap>",
|
|
111
|
-
` <loc>${escapeValueForXml(e.sitemap)}</loc>`,
|
|
112
|
-
// lastmod is optional
|
|
113
|
-
e.lastmod ? ` <lastmod>${escapeValueForXml(e.lastmod)}</lastmod>` : false,
|
|
114
|
-
" </sitemap>"
|
|
115
|
-
].filter(Boolean).join("\n")).join("\n");
|
|
116
|
-
const xmlParts = [
|
|
117
|
-
'<?xml version="1.0" encoding="UTF-8"?>'
|
|
118
|
-
];
|
|
119
|
-
if (xsl) {
|
|
120
|
-
let relativeBaseUrl = resolvers.relativeBaseUrlResolver?.(xsl) ?? xsl;
|
|
121
|
-
if (errorInfo && errorInfo.messages.length > 0) {
|
|
122
|
-
relativeBaseUrl = withQuery(relativeBaseUrl, {
|
|
123
|
-
errors: "true",
|
|
124
|
-
error_messages: errorInfo.messages,
|
|
125
|
-
error_urls: errorInfo.urls
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
xmlParts.push(`<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`);
|
|
129
|
-
}
|
|
130
|
-
xmlParts.push(
|
|
131
|
-
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
132
|
-
sitemapXml,
|
|
133
|
-
"</sitemapindex>"
|
|
134
|
-
);
|
|
135
|
-
if (credits) {
|
|
136
|
-
xmlParts.push(`<!-- XML Sitemap Index generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`);
|
|
137
|
-
}
|
|
138
|
-
return minify ? xmlParts.join("").replace(/(?<!<[^>]*)\s(?![^<]*>)/g, "") : xmlParts.join("\n");
|
|
139
|
-
}
|
|
140
107
|
export async function buildSitemapIndex(resolvers, runtimeConfig, nitro) {
|
|
141
108
|
if (!import.meta.dev && !import.meta.prerender && typeof runtimeConfig.cacheMaxAgeSeconds === "number" && runtimeConfig.cacheMaxAgeSeconds > 0 && resolvers.event) {
|
|
142
109
|
return buildSitemapIndexCached(resolvers.event, resolvers, runtimeConfig, nitro);
|
|
143
110
|
}
|
|
144
111
|
return buildSitemapIndexInternal(resolvers, runtimeConfig, nitro);
|
|
145
112
|
}
|
|
113
|
+
export { urlsToIndexXml, urlsToIndexXmlStream } from "./index-xml.js";
|
|
@@ -18,4 +18,4 @@ export declare function buildResolvedSitemapUrls(effectiveSitemap: SitemapDefini
|
|
|
18
18
|
export declare const buildResolvedSitemapUrlsCached: (_event: H3Event<import("h3").EventHandlerRequest>, effectiveSitemap: SitemapDefinition, matchName: string, isChunked: boolean, resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp | undefined) => Promise<ResolvedSitemapUrlsResult>;
|
|
19
19
|
export declare function getResolvedSitemapUrls(effectiveSitemap: SitemapDefinition, matchName: string, isChunked: boolean, resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<ResolvedSitemapUrlsResult>;
|
|
20
20
|
export declare function buildSitemapUrls(sitemap: SitemapDefinition, resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig, nitro?: NitroApp): Promise<ResolvedSitemapUrlsResult>;
|
|
21
|
-
export { urlsToXml } from './xml.js';
|
|
21
|
+
export { urlsToXml, urlsToXmlStream } from './xml.js';
|
|
@@ -14,30 +14,33 @@ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers, b
|
|
|
14
14
|
autoI18n,
|
|
15
15
|
isI18nMapped
|
|
16
16
|
} = runtimeConfig;
|
|
17
|
-
const
|
|
17
|
+
const hasFilters = !!sitemap.include?.length || !!sitemap.exclude?.length;
|
|
18
|
+
const filterPath = hasFilters ? createPathFilter({
|
|
18
19
|
include: sitemap.include,
|
|
19
20
|
exclude: sitemap.exclude
|
|
20
|
-
}, baseURL || "/");
|
|
21
|
-
const _urls =
|
|
21
|
+
}, baseURL || "/") : void 0;
|
|
22
|
+
const _urls = [];
|
|
23
|
+
for (const _e of urls) {
|
|
22
24
|
const e = preNormalizeEntry(_e, resolvers);
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}).filter(Boolean);
|
|
27
|
-
let validI18nUrlsForTransform = [];
|
|
25
|
+
if (e.loc && (!filterPath || filterPath(e.loc, e._path?.pathname)))
|
|
26
|
+
_urls.push(e);
|
|
27
|
+
}
|
|
28
28
|
const withoutPrefixPaths = {};
|
|
29
29
|
if (autoI18n && autoI18n.strategy !== "no_prefix") {
|
|
30
|
-
const localeCodes = autoI18n.locales.map((l) => l.code);
|
|
30
|
+
const localeCodes = new Set(autoI18n.locales.map((l) => l.code));
|
|
31
31
|
const localeByCode = new Map(autoI18n.locales.map((l) => [l.code, l]));
|
|
32
32
|
const isPrefixStrategy = autoI18n.strategy === "prefix";
|
|
33
33
|
const isPrefixExceptOrAndDefault = autoI18n.strategy === "prefix_and_default" || autoI18n.strategy === "prefix_except_default";
|
|
34
34
|
const xDefaultAndLocales = [{ code: "x-default", _hreflang: "x-default" }, ...autoI18n.locales];
|
|
35
35
|
const defaultLocale = autoI18n.defaultLocale;
|
|
36
36
|
const hasPages = !!autoI18n.pages;
|
|
37
|
+
const sortedPageKeys = hasPages ? Object.keys(autoI18n.pages).sort((a, b) => b.length - a.length) : void 0;
|
|
37
38
|
const hasDifferentDomains = !!autoI18n.differentDomains;
|
|
38
|
-
validI18nUrlsForTransform =
|
|
39
|
+
const validI18nUrlsForTransform = [];
|
|
40
|
+
for (let i = 0; i < _urls.length; i++) {
|
|
41
|
+
const _e = _urls[i];
|
|
39
42
|
if (_e._abs)
|
|
40
|
-
|
|
43
|
+
continue;
|
|
41
44
|
const split = splitForLocales(_e._relativeLoc, localeCodes);
|
|
42
45
|
let localeCode = split[0];
|
|
43
46
|
const pathWithoutPrefix = split[1];
|
|
@@ -47,31 +50,30 @@ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers, b
|
|
|
47
50
|
e._pathWithoutPrefix = pathWithoutPrefix;
|
|
48
51
|
const locale = localeByCode.get(localeCode);
|
|
49
52
|
if (!locale)
|
|
50
|
-
|
|
53
|
+
continue;
|
|
51
54
|
e._locale = locale;
|
|
52
55
|
e._index = i;
|
|
53
56
|
e._key = `${e._sitemap || ""}${e._path?.pathname || "/"}${e._path?.search || ""}`;
|
|
54
57
|
withoutPrefixPaths[pathWithoutPrefix] = withoutPrefixPaths[pathWithoutPrefix] || [];
|
|
55
58
|
if (!withoutPrefixPaths[pathWithoutPrefix].some((e2) => e2._locale.code === locale.code))
|
|
56
59
|
withoutPrefixPaths[pathWithoutPrefix].push(e);
|
|
57
|
-
|
|
58
|
-
}
|
|
60
|
+
validI18nUrlsForTransform.push(e);
|
|
61
|
+
}
|
|
59
62
|
for (const e of validI18nUrlsForTransform) {
|
|
60
63
|
if (!e._i18nTransform && !e.alternatives?.length) {
|
|
61
|
-
const alternatives =
|
|
62
|
-
|
|
64
|
+
const alternatives = [];
|
|
65
|
+
for (const u of withoutPrefixPaths[e._pathWithoutPrefix] || []) {
|
|
63
66
|
if (u._locale.code === defaultLocale) {
|
|
64
|
-
|
|
67
|
+
alternatives.push({
|
|
65
68
|
href: u.loc,
|
|
66
69
|
hreflang: "x-default"
|
|
67
70
|
});
|
|
68
71
|
}
|
|
69
|
-
|
|
72
|
+
alternatives.push({
|
|
70
73
|
href: u.loc,
|
|
71
74
|
hreflang: u._locale._hreflang || defaultLocale
|
|
72
75
|
});
|
|
73
|
-
|
|
74
|
-
}).flat().filter(Boolean);
|
|
76
|
+
}
|
|
75
77
|
if (alternatives.length)
|
|
76
78
|
e.alternatives = alternatives;
|
|
77
79
|
} else if (e._i18nTransform) {
|
|
@@ -91,7 +93,7 @@ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers, b
|
|
|
91
93
|
};
|
|
92
94
|
});
|
|
93
95
|
} else {
|
|
94
|
-
const pageMatch = hasPages ? findPageMapping(e._pathWithoutPrefix, autoI18n.pages) : null;
|
|
96
|
+
const pageMatch = hasPages ? findPageMapping(e._pathWithoutPrefix, autoI18n.pages, sortedPageKeys) : null;
|
|
95
97
|
const pathSearch = e._path?.search || "";
|
|
96
98
|
const pathWithoutPrefix = e._pathWithoutPrefix;
|
|
97
99
|
for (const l of autoI18n.locales) {
|
|
@@ -130,7 +132,7 @@ export function resolveSitemapEntries(sitemap, urls, runtimeConfig, resolvers, b
|
|
|
130
132
|
} else if (isPrefixExceptOrAndDefault && !isDefault) {
|
|
131
133
|
href = joinURL("/", code, pathWithoutPrefix);
|
|
132
134
|
}
|
|
133
|
-
if (!filterPath(href))
|
|
135
|
+
if (filterPath && !filterPath(href))
|
|
134
136
|
continue;
|
|
135
137
|
alternatives.push({
|
|
136
138
|
hreflang: locale._hreflang,
|
|
@@ -192,9 +194,17 @@ export async function buildResolvedSitemapUrls(effectiveSitemap, matchName, isCh
|
|
|
192
194
|
const localeSitemapKeys = isI18nMapped && autoI18n ? autoI18n.locales.map((l) => l._sitemap) : [];
|
|
193
195
|
if (isMultiSitemap) {
|
|
194
196
|
const sitemapNames = Object.keys(sitemaps).filter((k) => k !== "index");
|
|
197
|
+
const validSitemapNames = new Set(sitemapNames);
|
|
198
|
+
if (isI18nMapped) {
|
|
199
|
+
for (const name of sitemapNames) {
|
|
200
|
+
const localeKey = resolveI18nSitemapLocaleKey(name, localeSitemapKeys);
|
|
201
|
+
if (localeKey)
|
|
202
|
+
validSitemapNames.add(localeKey);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
195
205
|
const warnedSitemaps = nitro?._sitemapWarnedSitemaps || /* @__PURE__ */ new Set();
|
|
196
206
|
for (const e of enhancedUrls) {
|
|
197
|
-
const hasMatchingSitemap = typeof e._sitemap === "string" &&
|
|
207
|
+
const hasMatchingSitemap = typeof e._sitemap === "string" && validSitemapNames.has(e._sitemap);
|
|
198
208
|
if (typeof e._sitemap === "string" && !hasMatchingSitemap) {
|
|
199
209
|
if (!warnedSitemaps.has(e._sitemap)) {
|
|
200
210
|
warnedSitemaps.add(e._sitemap);
|
|
@@ -206,6 +216,7 @@ export async function buildResolvedSitemapUrls(effectiveSitemap, matchName, isCh
|
|
|
206
216
|
nitro._sitemapWarnedSitemaps = warnedSitemaps;
|
|
207
217
|
}
|
|
208
218
|
}
|
|
219
|
+
const matchedLocaleSitemap = isI18nMapped ? resolveI18nSitemapLocaleKey(matchName, localeSitemapKeys) : null;
|
|
209
220
|
const filteredUrls = enhancedUrls.filter((e) => {
|
|
210
221
|
if (e._sitemap === false)
|
|
211
222
|
return false;
|
|
@@ -215,7 +226,7 @@ export async function buildResolvedSitemapUrls(effectiveSitemap, matchName, isCh
|
|
|
215
226
|
if (e._sitemap === matchName)
|
|
216
227
|
return true;
|
|
217
228
|
if (isI18nMapped)
|
|
218
|
-
return e._sitemap ===
|
|
229
|
+
return e._sitemap === matchedLocaleSitemap;
|
|
219
230
|
return false;
|
|
220
231
|
}
|
|
221
232
|
return true;
|
|
@@ -273,4 +284,4 @@ export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig, nitro)
|
|
|
273
284
|
const urls = sliceUrlsForChunk(resolved.urls, sitemap.sitemapName, sitemaps, chunkSize);
|
|
274
285
|
return { urls, failedSources: resolved.failedSources };
|
|
275
286
|
}
|
|
276
|
-
export { urlsToXml } from "./xml.js";
|
|
287
|
+
export { urlsToXml, urlsToXmlStream } from "./xml.js";
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl } from '../../../types.js';
|
|
2
2
|
export declare function escapeValueForXml(value: boolean | string | number): string;
|
|
3
|
+
export declare function renderSitemapXmlChunks(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
4
|
+
messages: string[];
|
|
5
|
+
urls: string[];
|
|
6
|
+
}): Generator<string>;
|
|
3
7
|
export declare function urlsToXml(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, { version, xsl, credits, minify }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
4
8
|
messages: string[];
|
|
5
9
|
urls: string[];
|
|
6
10
|
}): string;
|
|
11
|
+
export declare function urlsToXmlStream(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, config: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
12
|
+
messages: string[];
|
|
13
|
+
urls: string[];
|
|
14
|
+
}): ReadableStream<Uint8Array>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { withQuery } from "ufo";
|
|
2
|
-
import { xmlEscape } from "
|
|
2
|
+
import { xmlEscape } from "../../../utils-pure.js";
|
|
3
|
+
import { createChunkedXmlStream } from "../stream.js";
|
|
3
4
|
export function escapeValueForXml(value) {
|
|
4
5
|
if (value === true || value === false)
|
|
5
6
|
return value ? "yes" : "no";
|
|
@@ -24,7 +25,10 @@ function buildUrlXml(url, NL, I1, I2, I3, I4) {
|
|
|
24
25
|
if (url.alternatives) {
|
|
25
26
|
for (const alt of url.alternatives) {
|
|
26
27
|
let attrs = "";
|
|
27
|
-
for (const
|
|
28
|
+
for (const k in alt) {
|
|
29
|
+
if (Object.hasOwn(alt, k))
|
|
30
|
+
attrs += ` ${k}="${xmlEscape(String(alt[k]))}"`;
|
|
31
|
+
}
|
|
28
32
|
xml += `${I2}<xhtml:link rel="alternate"${attrs} />${NL}`;
|
|
29
33
|
}
|
|
30
34
|
}
|
|
@@ -112,6 +116,30 @@ function buildUrlXml(url, NL, I1, I2, I3, I4) {
|
|
|
112
116
|
xml += `${I1}</url>`;
|
|
113
117
|
return xml;
|
|
114
118
|
}
|
|
119
|
+
export function* renderSitemapXmlChunks(urls, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
120
|
+
let xslHref = xsl ? resolvers.relativeBaseUrlResolver(xsl) : false;
|
|
121
|
+
if (xslHref && errorInfo?.messages.length) {
|
|
122
|
+
xslHref = withQuery(xslHref, {
|
|
123
|
+
errors: "true",
|
|
124
|
+
error_messages: errorInfo.messages,
|
|
125
|
+
error_urls: errorInfo.urls
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const NL = minify ? "" : "\n";
|
|
129
|
+
const I1 = minify ? "" : " ";
|
|
130
|
+
const I2 = minify ? "" : " ";
|
|
131
|
+
const I3 = minify ? "" : " ";
|
|
132
|
+
const I4 = minify ? "" : " ";
|
|
133
|
+
yield xslHref ? `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="${escapeValueForXml(xslHref)}"?>${NL}` : `<?xml version="1.0" encoding="UTF-8"?>${NL}`;
|
|
134
|
+
yield URLSET_OPENING_TAG + NL;
|
|
135
|
+
for (const url of urls) {
|
|
136
|
+
yield buildUrlXml(url, NL, I1, I2, I3, I4) + NL;
|
|
137
|
+
}
|
|
138
|
+
yield "</urlset>";
|
|
139
|
+
if (credits) {
|
|
140
|
+
yield `${NL}<!-- XML Sitemap generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
115
143
|
export function urlsToXml(urls, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
116
144
|
let xslHref = xsl ? resolvers.relativeBaseUrlResolver(xsl) : false;
|
|
117
145
|
if (xslHref && errorInfo?.messages.length) {
|
|
@@ -128,12 +156,13 @@ export function urlsToXml(urls, resolvers, { version, xsl, credits, minify }, er
|
|
|
128
156
|
const I4 = minify ? "" : " ";
|
|
129
157
|
let xml = xslHref ? `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="${escapeValueForXml(xslHref)}"?>${NL}` : `<?xml version="1.0" encoding="UTF-8"?>${NL}`;
|
|
130
158
|
xml += URLSET_OPENING_TAG + NL;
|
|
131
|
-
for (const url of urls)
|
|
159
|
+
for (const url of urls)
|
|
132
160
|
xml += buildUrlXml(url, NL, I1, I2, I3, I4) + NL;
|
|
133
|
-
}
|
|
134
161
|
xml += "</urlset>";
|
|
135
|
-
if (credits)
|
|
162
|
+
if (credits)
|
|
136
163
|
xml += `${NL}<!-- XML Sitemap generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`;
|
|
137
|
-
}
|
|
138
164
|
return xml;
|
|
139
165
|
}
|
|
166
|
+
export function urlsToXmlStream(urls, resolvers, config, errorInfo) {
|
|
167
|
+
return createChunkedXmlStream(renderSitemapXmlChunks(urls, resolvers, config, errorInfo));
|
|
168
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
|
-
export declare function sitemapXmlEventHandler(e: H3Event): Promise<string | void
|
|
3
|
-
export declare function sitemapIndexXmlEventHandler(e: H3Event): Promise<string
|
|
4
|
-
export declare function sitemapChildXmlEventHandler(e: H3Event): Promise<string | undefined>;
|
|
2
|
+
export declare function sitemapXmlEventHandler(e: H3Event): Promise<string | void | ReadableStream<Uint8Array<ArrayBufferLike>>>;
|
|
3
|
+
export declare function sitemapIndexXmlEventHandler(e: H3Event): Promise<string | ReadableStream<Uint8Array<ArrayBufferLike>>>;
|
|
4
|
+
export declare function sitemapChildXmlEventHandler(e: H3Event): Promise<string | ReadableStream<Uint8Array<ArrayBufferLike>> | undefined>;
|