@nuxtjs/sitemap 8.3.0 → 8.3.2
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/content.mjs +11 -8
- package/dist/devtools/lib/sitemap/state.ts +4 -1
- package/dist/devtools/pages/sitemap/index.vue +2 -2
- package/dist/module.json +2 -2
- package/dist/module.mjs +21 -42
- package/dist/runtime/server/routes/__sitemap__/debug-production.d.ts +3 -3
- package/dist/runtime/server/routes/__sitemap__/debug-production.js +43 -27
- package/dist/runtime/server/sitemap/builder/index-xml.js +13 -16
- package/dist/runtime/server/sitemap/builder/sitemap-index.js +13 -26
- package/dist/runtime/server/sitemap/builder/xml.d.ts +2 -2
- package/dist/runtime/server/sitemap/builder/xml.js +16 -19
- package/dist/runtime/server/sitemap/event-handlers.js +3 -16
- package/dist/runtime/server/sitemap/nitro.d.ts +1 -0
- package/dist/runtime/server/sitemap/nitro.js +20 -17
- package/dist/runtime/server/sitemap/urlset/normalise.js +11 -2
- package/dist/runtime/server/sitemap/urlset/sources.js +48 -7
- package/dist/runtime/utils-pure.js +2 -7
- package/dist/utils.d.mts +58 -19
- package/dist/utils.d.ts +58 -19
- package/dist/utils.mjs +171 -955
- package/package.json +25 -27
- package/dist/runtime/server/kit.d.ts +0 -2
- package/dist/runtime/server/kit.js +0 -30
package/dist/content.mjs
CHANGED
|
@@ -7,6 +7,16 @@ if (!globalThis.__sitemapCollectionOnUrlFns)
|
|
|
7
7
|
globalThis.__sitemapCollectionOnUrlFns = /* @__PURE__ */ new Map();
|
|
8
8
|
const collectionFilters = globalThis.__sitemapCollectionFilters;
|
|
9
9
|
const collectionOnUrlFns = globalThis.__sitemapCollectionOnUrlFns;
|
|
10
|
+
function registerCollectionHooks(options, callerName) {
|
|
11
|
+
if (!options?.filter && !options?.onUrl)
|
|
12
|
+
return;
|
|
13
|
+
if (!options.name)
|
|
14
|
+
throw new Error(`[sitemap] \`name\` is required when using \`filter\` or \`onUrl\` in ${callerName}()`);
|
|
15
|
+
if (options.filter)
|
|
16
|
+
collectionFilters.set(options.name, options.filter);
|
|
17
|
+
if (options.onUrl)
|
|
18
|
+
collectionOnUrlFns.set(options.name, options.onUrl);
|
|
19
|
+
}
|
|
10
20
|
const { defineSchema, asCollection, schema } = createContentSchemaFactory({
|
|
11
21
|
fieldName: "sitemap",
|
|
12
22
|
label: "sitemap",
|
|
@@ -16,14 +26,7 @@ const { defineSchema, asCollection, schema } = createContentSchemaFactory({
|
|
|
16
26
|
if ("type" in options || "source" in options)
|
|
17
27
|
throw new Error("[sitemap] `defineSitemapSchema()` returns a schema field, not a collection wrapper. Use it inside your schema: `schema: z.object({ sitemap: defineSitemapSchema() })`. See https://nuxtseo.com/sitemap/guides/content");
|
|
18
28
|
warnIfZodMismatch(options?.z);
|
|
19
|
-
|
|
20
|
-
if (!options.name)
|
|
21
|
-
throw new Error("[sitemap] `name` is required when using `filter` or `onUrl` in defineSitemapSchema()");
|
|
22
|
-
if (options.filter)
|
|
23
|
-
collectionFilters.set(options.name, options.filter);
|
|
24
|
-
if (options.onUrl)
|
|
25
|
-
collectionOnUrlFns.set(options.name, options.onUrl);
|
|
26
|
-
}
|
|
29
|
+
registerCollectionHooks(options, "defineSitemapSchema");
|
|
27
30
|
}
|
|
28
31
|
}, z);
|
|
29
32
|
function asSitemapCollection(collection, options) {
|
|
@@ -35,7 +35,10 @@ export async function refreshProductionData() {
|
|
|
35
35
|
// Try fetching the full debug endpoint from production first (proxied through local server)
|
|
36
36
|
const remoteDebug = await appFetch.value('/__sitemap__/debug-production.json', {
|
|
37
37
|
query: { url: productionUrl.value, mode: 'debug' },
|
|
38
|
-
}).catch(() =>
|
|
38
|
+
}).catch(() => {
|
|
39
|
+
// Production debug is optional; use the public sitemap XML fallback below.
|
|
40
|
+
return null
|
|
41
|
+
}) as (typeof data.value & { error?: string }) | null
|
|
39
42
|
if (remoteDebug && !remoteDebug.error && remoteDebug.sitemaps && !Array.isArray(remoteDebug.sitemaps)) {
|
|
40
43
|
// Response has object sitemaps (debug.json format) rather than array (XML fallback format)
|
|
41
44
|
productionRemoteDebugData.value = remoteDebug
|
|
@@ -137,7 +137,7 @@ const totalProductionWarnings = computed(() =>
|
|
|
137
137
|
target="_blank"
|
|
138
138
|
class="link-external"
|
|
139
139
|
>
|
|
140
|
-
Learn more
|
|
140
|
+
Learn more about large sitemaps
|
|
141
141
|
</a>
|
|
142
142
|
</DevtoolsAlert>
|
|
143
143
|
</template>
|
|
@@ -320,7 +320,7 @@ const totalProductionWarnings = computed(() =>
|
|
|
320
320
|
target="_blank"
|
|
321
321
|
class="link-external"
|
|
322
322
|
>
|
|
323
|
-
Learn more
|
|
323
|
+
Learn more about large sitemaps
|
|
324
324
|
</a>
|
|
325
325
|
</DevtoolsAlert>
|
|
326
326
|
</template>
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { useNuxt, hasNuxtModule, addTypeTemplate, addTemplate, defineNuxtModule, createResolver,
|
|
1
|
+
import { useNuxt, hasNuxtModule, addTypeTemplate, addTemplate, defineNuxtModule, createResolver, getNuxtModuleVersion, hasNuxtModuleCompatibility, addServerImports, addPrerenderRoutes, addServerPlugin, resolveModule, addServerHandler } from '@nuxt/kit';
|
|
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, useModuleLogger, getNuxtModuleOptions, resolveNitroPreset, resolveNuxtContentVersion, createPagesPromise, createNitroPromise } from 'nuxtseo-shared/kit';
|
|
6
|
+
import { serializeFilters } from 'nuxtseo-shared/utils';
|
|
5
7
|
import { dirname, extname } from 'pathe';
|
|
6
8
|
import { readPackageJSON } from 'pkg-types';
|
|
7
9
|
import { withBase, withHttps, withTrailingSlash, withoutLeadingSlash, joinURL, withLeadingSlash, withoutTrailingSlash } from 'ufo';
|
|
@@ -11,7 +13,6 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
11
13
|
import { join } from 'node:path';
|
|
12
14
|
import { colors } from 'consola/utils';
|
|
13
15
|
import { splitForLocales, createPathFilter } from '../dist/runtime/utils-pure.js';
|
|
14
|
-
import { isNuxtGenerate, getNuxtModuleOptions, resolveNitroPreset, resolveNuxtContentVersion, createPagesPromise, createNitroPromise } from 'nuxtseo-shared/kit';
|
|
15
16
|
import { p as parseHtmlExtractSitemapMeta } from './shared/sitemap.BoMnWHOt.mjs';
|
|
16
17
|
import { normaliseDate } from '../dist/runtime/server/sitemap/urlset/normalise.js';
|
|
17
18
|
import { splitPathForI18nLocales as splitPathForI18nLocales$1, expandCompactLocaleRoute, normalizeLocales, generatePathForI18nPages } from 'nuxtseo-shared/i18n';
|
|
@@ -242,6 +243,10 @@ declare module 'nuxt/app' {
|
|
|
242
243
|
export {}
|
|
243
244
|
`;
|
|
244
245
|
}
|
|
246
|
+
}, {
|
|
247
|
+
nitro: true,
|
|
248
|
+
node: true,
|
|
249
|
+
nuxt: true
|
|
245
250
|
});
|
|
246
251
|
addTemplate({
|
|
247
252
|
filename: "types/nuxt-sitemap-virtual.d.ts",
|
|
@@ -272,25 +277,6 @@ declare module '#sitemap/content-on-url' {
|
|
|
272
277
|
});
|
|
273
278
|
}
|
|
274
279
|
|
|
275
|
-
function isValidFilter(filter) {
|
|
276
|
-
if (typeof filter === "string")
|
|
277
|
-
return true;
|
|
278
|
-
if (filter instanceof RegExp)
|
|
279
|
-
return true;
|
|
280
|
-
if (typeof filter === "object" && typeof filter.regex === "string")
|
|
281
|
-
return true;
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
function normalizeFilters(filters) {
|
|
285
|
-
return (filters || []).map((filter) => {
|
|
286
|
-
if (!isValidFilter(filter)) {
|
|
287
|
-
console.warn(`[@nuxtjs/sitemap] You have provided an invalid filter: ${filter}, ignoring.`);
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
return filter instanceof RegExp ? { regex: filter.toString() } : filter;
|
|
291
|
-
}).filter(Boolean);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
280
|
function splitPathForI18nLocales(path, autoI18n) {
|
|
295
281
|
if (typeof path !== "string")
|
|
296
282
|
return path;
|
|
@@ -462,8 +448,7 @@ function generateExtraRoutesFromNuxtConfig(nuxt = useNuxt()) {
|
|
|
462
448
|
return false;
|
|
463
449
|
return !v.redirect;
|
|
464
450
|
}).map(([k]) => k).filter(filterForValidPage);
|
|
465
|
-
|
|
466
|
-
return { routeRules, prerenderUrls };
|
|
451
|
+
return { routeRules };
|
|
467
452
|
}
|
|
468
453
|
|
|
469
454
|
const module$1 = defineNuxtModule({
|
|
@@ -528,8 +513,7 @@ const module$1 = defineNuxtModule({
|
|
|
528
513
|
async setup(config, nuxt) {
|
|
529
514
|
const { resolve } = createResolver(import.meta.url);
|
|
530
515
|
const { name, version } = await readPackageJSON(resolve("../package.json"));
|
|
531
|
-
const logger =
|
|
532
|
-
logger.level = config.debug || nuxt.options.debug ? 4 : 3;
|
|
516
|
+
const logger = useModuleLogger(name, config, nuxt);
|
|
533
517
|
if (config.enabled === false) {
|
|
534
518
|
logger.debug("The module is disabled, skipping setup.");
|
|
535
519
|
return;
|
|
@@ -1046,8 +1030,8 @@ ${onUrlEntries.join("\n")}`;
|
|
|
1046
1030
|
}
|
|
1047
1031
|
for (const sitemapName in sitemaps) {
|
|
1048
1032
|
const sitemap = sitemaps[sitemapName];
|
|
1049
|
-
sitemap.include =
|
|
1050
|
-
sitemap.exclude =
|
|
1033
|
+
sitemap.include = serializeFilters(sitemap.include || [], "@nuxtjs/sitemap");
|
|
1034
|
+
sitemap.exclude = serializeFilters(sitemap.exclude || [], "@nuxtjs/sitemap");
|
|
1051
1035
|
}
|
|
1052
1036
|
const runtimeConfig = {
|
|
1053
1037
|
isI18nMapped,
|
|
@@ -1153,8 +1137,8 @@ ${onUrlEntries.join("\n")}`;
|
|
|
1153
1137
|
routesNameSeparator: nuxtI18nConfig.routesNameSeparator,
|
|
1154
1138
|
normalisedLocales,
|
|
1155
1139
|
filter: {
|
|
1156
|
-
include:
|
|
1157
|
-
exclude:
|
|
1140
|
+
include: serializeFilters(config.include || [], "@nuxtjs/sitemap"),
|
|
1141
|
+
exclude: serializeFilters(config.exclude || [], "@nuxtjs/sitemap")
|
|
1158
1142
|
},
|
|
1159
1143
|
autoI18n: !!resolvedAutoI18n
|
|
1160
1144
|
});
|
|
@@ -1286,19 +1270,14 @@ export async function readSourcesFromFilesystem() {
|
|
|
1286
1270
|
}
|
|
1287
1271
|
`;
|
|
1288
1272
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
nitroConfig.virtual[`#sitemap-virtual/child-sources.mjs`] = async () => {
|
|
1298
|
-
const childSources = await generateChildSources();
|
|
1299
|
-
return `export const sources = ${JSON.stringify(childSources, null, 4)}`;
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1273
|
+
nitroConfig.virtual["#sitemap-virtual/global-sources.mjs"] = async () => {
|
|
1274
|
+
const globalSources = await generateGlobalSources();
|
|
1275
|
+
return `export const sources = ${JSON.stringify(globalSources, null, 4)}`;
|
|
1276
|
+
};
|
|
1277
|
+
nitroConfig.virtual[`#sitemap-virtual/child-sources.mjs`] = async () => {
|
|
1278
|
+
const childSources = await generateChildSources();
|
|
1279
|
+
return `export const sources = ${JSON.stringify(childSources, null, 4)}`;
|
|
1280
|
+
};
|
|
1302
1281
|
});
|
|
1303
1282
|
if (config.xsl === "/__sitemap__/style.xsl") {
|
|
1304
1283
|
addServerHandler({
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SitemapIssue } from 'sitemapd/parse';
|
|
2
2
|
export interface ProductionSitemapEntry {
|
|
3
3
|
loc: string;
|
|
4
4
|
urlCount: number;
|
|
5
|
-
warnings:
|
|
5
|
+
warnings: SitemapIssue[];
|
|
6
6
|
error?: string;
|
|
7
7
|
lastmod?: string;
|
|
8
8
|
}
|
|
@@ -10,7 +10,7 @@ export interface ProductionDebugResponse {
|
|
|
10
10
|
url: string;
|
|
11
11
|
isIndex: boolean;
|
|
12
12
|
sitemaps: ProductionSitemapEntry[];
|
|
13
|
-
warnings:
|
|
13
|
+
warnings: SitemapIssue[];
|
|
14
14
|
error?: string;
|
|
15
15
|
}
|
|
16
16
|
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, any> | ProductionDebugResponse>>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { isSitemapIndex, parseSitemapIndex, parseSitemapXml } from "@nuxtjs/sitemap/utils";
|
|
2
1
|
import { defineEventHandler, getQuery } from "h3";
|
|
3
|
-
|
|
2
|
+
import { collectSitemap } from "sitemapd/parse";
|
|
3
|
+
async function fetchSitemapBody(url) {
|
|
4
4
|
const response = await fetch(url, {
|
|
5
|
-
headers: { Accept: "application/xml, text/xml" },
|
|
5
|
+
headers: { Accept: "application/xml, text/xml, application/gzip" },
|
|
6
6
|
signal: AbortSignal.timeout(15e3)
|
|
7
7
|
});
|
|
8
8
|
if (!response.ok)
|
|
9
9
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
10
|
-
return response.
|
|
10
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
11
11
|
}
|
|
12
12
|
export default defineEventHandler(async (e) => {
|
|
13
13
|
const { url, mode } = getQuery(e);
|
|
@@ -18,58 +18,74 @@ export default defineEventHandler(async (e) => {
|
|
|
18
18
|
const response = await fetch(debugUrl, {
|
|
19
19
|
headers: { Accept: "application/json" },
|
|
20
20
|
signal: AbortSignal.timeout(1e4)
|
|
21
|
-
}).catch(() =>
|
|
21
|
+
}).catch(() => {
|
|
22
|
+
return null;
|
|
23
|
+
});
|
|
22
24
|
if (response?.ok) {
|
|
23
|
-
const json = await response.json().catch(() =>
|
|
25
|
+
const json = await response.json().catch(() => {
|
|
26
|
+
return null;
|
|
27
|
+
});
|
|
24
28
|
if (json?.sitemaps)
|
|
25
29
|
return json;
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
const sitemapUrl = url.endsWith("/") ? `${url}sitemap.xml` : url;
|
|
29
|
-
const
|
|
33
|
+
const body = await fetchSitemapBody(sitemapUrl).catch((err) => {
|
|
30
34
|
return err;
|
|
31
35
|
});
|
|
32
|
-
if (
|
|
33
|
-
return { url: sitemapUrl, isIndex: false, sitemaps: [], warnings: [], error: `Failed to fetch sitemap: ${
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
if (body instanceof Error)
|
|
37
|
+
return { url: sitemapUrl, isIndex: false, sitemaps: [], warnings: [], error: `Failed to fetch sitemap: ${body.message}` };
|
|
38
|
+
const parsed = await collectSitemap(body);
|
|
39
|
+
if (parsed._tag !== "document") {
|
|
40
|
+
return {
|
|
41
|
+
url: sitemapUrl,
|
|
42
|
+
isIndex: false,
|
|
43
|
+
sitemaps: [],
|
|
44
|
+
warnings: parsed.issues,
|
|
45
|
+
error: parsed.issues.map((issue) => issue.message).join("; ") || "Invalid sitemap document"
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (parsed.document._tag === "index") {
|
|
49
|
+
const { entries } = parsed.document;
|
|
36
50
|
const sitemaps = await Promise.all(
|
|
37
51
|
entries.map(async (entry) => {
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
52
|
+
const childBody = await fetchSitemapBody(entry.loc).catch((err) => err);
|
|
53
|
+
if (childBody instanceof Error) {
|
|
40
54
|
return {
|
|
41
55
|
loc: entry.loc,
|
|
42
56
|
urlCount: 0,
|
|
43
57
|
warnings: [],
|
|
44
|
-
error:
|
|
58
|
+
error: childBody.message,
|
|
59
|
+
lastmod: entry.lastmod
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const result = await collectSitemap(childBody);
|
|
63
|
+
if (result._tag !== "document" || result.document._tag !== "urlset") {
|
|
64
|
+
return {
|
|
65
|
+
loc: entry.loc,
|
|
66
|
+
urlCount: 0,
|
|
67
|
+
warnings: result.issues,
|
|
68
|
+
error: result.issues.map((issue) => issue.message).join("; ") || "Child is not a URL set",
|
|
45
69
|
lastmod: entry.lastmod
|
|
46
70
|
};
|
|
47
71
|
}
|
|
48
|
-
const result2 = await parseSitemapXml(childXml).catch((err) => ({
|
|
49
|
-
urls: [],
|
|
50
|
-
warnings: [{ type: "validation", message: err.message }]
|
|
51
|
-
}));
|
|
52
72
|
return {
|
|
53
73
|
loc: entry.loc,
|
|
54
|
-
urlCount:
|
|
55
|
-
warnings:
|
|
74
|
+
urlCount: result.document.entries.length,
|
|
75
|
+
warnings: result.issues,
|
|
56
76
|
lastmod: entry.lastmod
|
|
57
77
|
};
|
|
58
78
|
})
|
|
59
79
|
);
|
|
60
|
-
return { url: sitemapUrl, isIndex: true, sitemaps, warnings };
|
|
80
|
+
return { url: sitemapUrl, isIndex: true, sitemaps, warnings: parsed.issues };
|
|
61
81
|
}
|
|
62
|
-
const result = await parseSitemapXml(xml).catch((err) => ({
|
|
63
|
-
urls: [],
|
|
64
|
-
warnings: [{ type: "validation", message: err.message }]
|
|
65
|
-
}));
|
|
66
82
|
return {
|
|
67
83
|
url: sitemapUrl,
|
|
68
84
|
isIndex: false,
|
|
69
85
|
sitemaps: [{
|
|
70
86
|
loc: sitemapUrl,
|
|
71
|
-
urlCount:
|
|
72
|
-
warnings:
|
|
87
|
+
urlCount: parsed.document.entries.length,
|
|
88
|
+
warnings: parsed.issues
|
|
73
89
|
}],
|
|
74
90
|
warnings: []
|
|
75
91
|
};
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { withQuery } from "ufo";
|
|
2
2
|
import { createChunkedXmlStream } from "../stream.js";
|
|
3
3
|
import { escapeValueForXml } from "./xml.js";
|
|
4
|
+
function resolveIndexXslHref(resolvers, xsl, errorInfo) {
|
|
5
|
+
let relativeBaseUrl = resolvers.relativeBaseUrlResolver?.(xsl) ?? xsl;
|
|
6
|
+
if (errorInfo && errorInfo.messages.length > 0) {
|
|
7
|
+
relativeBaseUrl = withQuery(relativeBaseUrl, {
|
|
8
|
+
errors: "true",
|
|
9
|
+
error_messages: errorInfo.messages,
|
|
10
|
+
error_urls: errorInfo.urls
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
return relativeBaseUrl;
|
|
14
|
+
}
|
|
4
15
|
export function* renderSitemapIndexXmlChunks(sitemaps, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
5
16
|
const NL = minify ? "" : "\n";
|
|
6
17
|
const I1 = minify ? "" : " ";
|
|
7
18
|
const I2 = minify ? "" : " ";
|
|
8
19
|
yield '<?xml version="1.0" encoding="UTF-8"?>';
|
|
9
20
|
if (xsl) {
|
|
10
|
-
|
|
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
|
-
}
|
|
21
|
+
const relativeBaseUrl = resolveIndexXslHref(resolvers, xsl, errorInfo);
|
|
18
22
|
yield `${NL}<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`;
|
|
19
23
|
}
|
|
20
24
|
yield `${NL}<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${NL}`;
|
|
@@ -46,14 +50,7 @@ export function urlsToIndexXml(sitemaps, resolvers, { version, xsl, credits, min
|
|
|
46
50
|
}
|
|
47
51
|
const xmlParts = ['<?xml version="1.0" encoding="UTF-8"?>'];
|
|
48
52
|
if (xsl) {
|
|
49
|
-
|
|
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
|
-
}
|
|
53
|
+
const relativeBaseUrl = resolveIndexXslHref(resolvers, xsl, errorInfo);
|
|
57
54
|
xmlParts.push(`<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`);
|
|
58
55
|
}
|
|
59
56
|
xmlParts.push(
|
|
@@ -48,22 +48,7 @@ async function buildSitemapIndexInternal(resolvers, runtimeConfig, nitro) {
|
|
|
48
48
|
}
|
|
49
49
|
const indexLastmod = autoLastmod ? normaliseDate(/* @__PURE__ */ new Date()) : void 0;
|
|
50
50
|
const entries = [];
|
|
51
|
-
|
|
52
|
-
const sitemap = sitemaps.chunks;
|
|
53
|
-
const resolved = await getResolvedSitemapUrls(sitemap, "sitemap", true, resolvers, runtimeConfig, nitro);
|
|
54
|
-
allFailedSources.push(...resolved.failedSources);
|
|
55
|
-
const chunkCount = Math.ceil(resolved.urls.length / defaultSitemapsChunkSize);
|
|
56
|
-
for (let i = 0; i < chunkCount; i++) {
|
|
57
|
-
const entry = {
|
|
58
|
-
_sitemapName: String(i),
|
|
59
|
-
sitemap: resolvers.canonicalUrlResolver(joinURL(sitemapsPathPrefix || "", `/${i}.xml`))
|
|
60
|
-
};
|
|
61
|
-
if (indexLastmod)
|
|
62
|
-
entry.lastmod = indexLastmod;
|
|
63
|
-
entries.push(entry);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
for (const name of nonChunkedNames) {
|
|
51
|
+
const pushEntry = (name) => {
|
|
67
52
|
const entry = {
|
|
68
53
|
_sitemapName: name,
|
|
69
54
|
sitemap: resolvers.canonicalUrlResolver(joinURL(sitemapsPathPrefix || "", `/${name}.xml`))
|
|
@@ -71,7 +56,17 @@ async function buildSitemapIndexInternal(resolvers, runtimeConfig, nitro) {
|
|
|
71
56
|
if (indexLastmod)
|
|
72
57
|
entry.lastmod = indexLastmod;
|
|
73
58
|
entries.push(entry);
|
|
59
|
+
};
|
|
60
|
+
if (typeof sitemaps.chunks !== "undefined") {
|
|
61
|
+
const sitemap = sitemaps.chunks;
|
|
62
|
+
const resolved = await getResolvedSitemapUrls(sitemap, "sitemap", true, resolvers, runtimeConfig, nitro);
|
|
63
|
+
allFailedSources.push(...resolved.failedSources);
|
|
64
|
+
const chunkCount = Math.ceil(resolved.urls.length / defaultSitemapsChunkSize);
|
|
65
|
+
for (let i = 0; i < chunkCount; i++)
|
|
66
|
+
pushEntry(String(i));
|
|
74
67
|
}
|
|
68
|
+
for (const name of nonChunkedNames)
|
|
69
|
+
pushEntry(name);
|
|
75
70
|
for (const sitemapName in sitemaps) {
|
|
76
71
|
const sitemapConfig = sitemaps[sitemapName];
|
|
77
72
|
if (sitemapName !== "index" && sitemapConfig._isChunking) {
|
|
@@ -85,16 +80,8 @@ async function buildSitemapIndexInternal(resolvers, runtimeConfig, nitro) {
|
|
|
85
80
|
chunkCount = Math.ceil(resolved.urls.length / chunkSize);
|
|
86
81
|
}
|
|
87
82
|
sitemapConfig._chunkCount = chunkCount;
|
|
88
|
-
for (let i = 0; i < chunkCount; i++)
|
|
89
|
-
|
|
90
|
-
const entry = {
|
|
91
|
-
_sitemapName: chunkName,
|
|
92
|
-
sitemap: resolvers.canonicalUrlResolver(joinURL(sitemapsPathPrefix || "", `/${chunkName}.xml`))
|
|
93
|
-
};
|
|
94
|
-
if (indexLastmod)
|
|
95
|
-
entry.lastmod = indexLastmod;
|
|
96
|
-
entries.push(entry);
|
|
97
|
-
}
|
|
83
|
+
for (let i = 0; i < chunkCount; i++)
|
|
84
|
+
pushEntry(`${sitemapName}-${i}`);
|
|
98
85
|
}
|
|
99
86
|
}
|
|
100
87
|
if (sitemaps.index) {
|
|
@@ -1,10 +1,10 @@
|
|
|
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,
|
|
3
|
+
export declare function renderSitemapXmlChunks(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, config: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
4
4
|
messages: string[];
|
|
5
5
|
urls: string[];
|
|
6
6
|
}): Generator<string>;
|
|
7
|
-
export declare function urlsToXml(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers,
|
|
7
|
+
export declare function urlsToXml(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, config: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits' | 'minify'>, errorInfo?: {
|
|
8
8
|
messages: string[];
|
|
9
9
|
urls: string[];
|
|
10
10
|
}): string;
|
|
@@ -116,7 +116,7 @@ function buildUrlXml(url, NL, I1, I2, I3, I4) {
|
|
|
116
116
|
xml += `${I1}</url>`;
|
|
117
117
|
return xml;
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
function resolveXmlRenderContext(resolvers, { xsl, minify }, errorInfo) {
|
|
120
120
|
let xslHref = xsl ? resolvers.relativeBaseUrlResolver(xsl) : false;
|
|
121
121
|
if (xslHref && errorInfo?.messages.length) {
|
|
122
122
|
xslHref = withQuery(xslHref, {
|
|
@@ -126,10 +126,18 @@ export function* renderSitemapXmlChunks(urls, resolvers, { version, xsl, credits
|
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
128
|
const NL = minify ? "" : "\n";
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
return {
|
|
130
|
+
xslHref,
|
|
131
|
+
NL,
|
|
132
|
+
I1: minify ? "" : " ",
|
|
133
|
+
I2: minify ? "" : " ",
|
|
134
|
+
I3: minify ? "" : " ",
|
|
135
|
+
I4: minify ? "" : " "
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function* renderSitemapXmlChunks(urls, resolvers, config, errorInfo) {
|
|
139
|
+
const { version, credits } = config;
|
|
140
|
+
const { xslHref, NL, I1, I2, I3, I4 } = resolveXmlRenderContext(resolvers, config, errorInfo);
|
|
133
141
|
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
142
|
yield URLSET_OPENING_TAG + NL;
|
|
135
143
|
for (const url of urls) {
|
|
@@ -140,20 +148,9 @@ export function* renderSitemapXmlChunks(urls, resolvers, { version, xsl, credits
|
|
|
140
148
|
yield `${NL}<!-- XML Sitemap generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`;
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
|
-
export function urlsToXml(urls, resolvers,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
xslHref = withQuery(xslHref, {
|
|
147
|
-
errors: "true",
|
|
148
|
-
error_messages: errorInfo.messages,
|
|
149
|
-
error_urls: errorInfo.urls
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
const NL = minify ? "" : "\n";
|
|
153
|
-
const I1 = minify ? "" : " ";
|
|
154
|
-
const I2 = minify ? "" : " ";
|
|
155
|
-
const I3 = minify ? "" : " ";
|
|
156
|
-
const I4 = minify ? "" : " ";
|
|
151
|
+
export function urlsToXml(urls, resolvers, config, errorInfo) {
|
|
152
|
+
const { version, credits } = config;
|
|
153
|
+
const { xslHref, NL, I1, I2, I3, I4 } = resolveXmlRenderContext(resolvers, config, errorInfo);
|
|
157
154
|
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}`;
|
|
158
155
|
xml += URLSET_OPENING_TAG + NL;
|
|
159
156
|
for (const url of urls)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { appendHeader, createError, getRequestURL, getRouterParam, sendRedirect
|
|
1
|
+
import { appendHeader, createError, getRequestURL, getRouterParam, sendRedirect } from "h3";
|
|
2
2
|
import { useNitroApp, useRuntimeConfig } from "nitropack/runtime";
|
|
3
3
|
import { joinURL, withBase, withLeadingSlash, withoutLeadingSlash, withoutTrailingSlash } from "ufo";
|
|
4
4
|
import { useSitemapRuntimeConfig } from "../utils.js";
|
|
5
5
|
import { urlsToIndexXml, urlsToIndexXmlStream } from "./builder/index-xml.js";
|
|
6
6
|
import { buildSitemapIndex } from "./builder/sitemap-index.js";
|
|
7
|
-
import { createSitemap, renderSitemapOutput, useNitroUrlResolvers } from "./nitro.js";
|
|
7
|
+
import { createSitemap, renderSitemapOutput, setSitemapResponseHeaders, useNitroUrlResolvers } from "./nitro.js";
|
|
8
8
|
import { getSitemapConfig, parseChunkInfo } from "./utils/chunk.js";
|
|
9
9
|
export async function sitemapXmlEventHandler(e) {
|
|
10
10
|
const runtimeConfig = useSitemapRuntimeConfig();
|
|
@@ -37,20 +37,7 @@ export async function sitemapIndexXmlEventHandler(e) {
|
|
|
37
37
|
!!runtimeConfig.experimentalStreaming && !import.meta.prerender,
|
|
38
38
|
runtimeConfig.debug
|
|
39
39
|
);
|
|
40
|
-
|
|
41
|
-
if (runtimeConfig.cacheMaxAgeSeconds) {
|
|
42
|
-
setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
|
|
43
|
-
const now = /* @__PURE__ */ new Date();
|
|
44
|
-
setHeader(e, "X-Sitemap-Generated", now.toISOString());
|
|
45
|
-
setHeader(e, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
|
|
46
|
-
const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
|
|
47
|
-
setHeader(e, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
|
|
48
|
-
const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
|
|
49
|
-
setHeader(e, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
|
|
50
|
-
} else {
|
|
51
|
-
setHeader(e, "Cache-Control", `no-cache, no-store`);
|
|
52
|
-
}
|
|
53
|
-
e.context._isSitemap = true;
|
|
40
|
+
setSitemapResponseHeaders(e, runtimeConfig);
|
|
54
41
|
return output;
|
|
55
42
|
}
|
|
56
43
|
export async function sitemapChildXmlEventHandler(e) {
|
|
@@ -3,4 +3,5 @@ import type { NitroApp } from 'nitropack/types';
|
|
|
3
3
|
import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapDefinition } from '../../types.js';
|
|
4
4
|
export declare function useNitroUrlResolvers(e: H3Event): NitroUrlResolvers;
|
|
5
5
|
export declare function renderSitemapOutput(nitro: NitroApp, event: H3Event, sitemapName: string, renderString: () => string, renderStream: () => ReadableStream<Uint8Array>, shouldStream: boolean, debug: boolean): Promise<string | ReadableStream<Uint8Array>>;
|
|
6
|
+
export declare function setSitemapResponseHeaders(event: H3Event, runtimeConfig: ModuleRuntimeConfig): void;
|
|
6
7
|
export declare function createSitemap(event: H3Event, definition: SitemapDefinition, runtimeConfig: ModuleRuntimeConfig): Promise<string | ReadableStream<Uint8Array<ArrayBufferLike>>>;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { defu } from "defu";
|
|
2
2
|
import { createError, getHeader, getQuery, setHeader } from "h3";
|
|
3
|
-
import { defineCachedFunction, useNitroApp } from "nitropack/runtime";
|
|
3
|
+
import { defineCachedFunction, useNitroApp, useRuntimeConfig } from "nitropack/runtime";
|
|
4
4
|
import { fixSlashes } from "nuxt-site-config/urls";
|
|
5
|
+
import { createNitroRouteRuleMatcher } from "nuxtseo-shared/server";
|
|
5
6
|
import { getPathRobotConfig } from "#internal/nuxt-robots/getPathRobotConfig";
|
|
6
7
|
import { getSiteConfig } from "#site-config/server/composables/getSiteConfig";
|
|
7
8
|
import { createSitePathResolver } from "#site-config/server/composables/utils";
|
|
8
9
|
import staticConfig from "#sitemap-virtual/static-config.mjs";
|
|
9
10
|
import { logger, mergeOnKey, splitForLocales } from "../../utils-pure.js";
|
|
10
|
-
import { createNitroRouteRuleMatcher } from "../kit.js";
|
|
11
11
|
import { buildSitemapUrls, urlsToXml, urlsToXmlStream } from "./builder/sitemap.js";
|
|
12
12
|
import { createChunkedXmlStream } from "./stream.js";
|
|
13
13
|
import { normaliseEntry, preNormalizeEntry } from "./urlset/normalise.js";
|
|
@@ -52,7 +52,7 @@ async function buildSitemapRenderPlan(event, definition, resolvers, runtimeConfi
|
|
|
52
52
|
message: `Sitemap generation failed due to ${failedSources.length} failed sources: ${failedSources.map((s) => `"${s.url}" (${s.error})`).join(", ")}`
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
const routeRuleMatcher = createNitroRouteRuleMatcher();
|
|
55
|
+
const routeRuleMatcher = createNitroRouteRuleMatcher(useRuntimeConfig(event));
|
|
56
56
|
const { autoI18n } = runtimeConfig;
|
|
57
57
|
const localeCodes = autoI18n?.locales && autoI18n.strategy !== "no_prefix" ? new Set(autoI18n.locales.map((l) => l.code)) : void 0;
|
|
58
58
|
const sourceCount = sitemapUrls.length;
|
|
@@ -205,6 +205,22 @@ const buildSitemapXmlCached = defineCachedFunction(
|
|
|
205
205
|
// Enable stale-while-revalidate
|
|
206
206
|
}
|
|
207
207
|
);
|
|
208
|
+
export function setSitemapResponseHeaders(event, runtimeConfig) {
|
|
209
|
+
setHeader(event, "Content-Type", "text/xml; charset=UTF-8");
|
|
210
|
+
if (runtimeConfig.cacheMaxAgeSeconds) {
|
|
211
|
+
setHeader(event, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
|
|
212
|
+
const now = /* @__PURE__ */ new Date();
|
|
213
|
+
setHeader(event, "X-Sitemap-Generated", now.toISOString());
|
|
214
|
+
setHeader(event, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
|
|
215
|
+
const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
|
|
216
|
+
setHeader(event, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
|
|
217
|
+
const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
|
|
218
|
+
setHeader(event, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
|
|
219
|
+
} else {
|
|
220
|
+
setHeader(event, "Cache-Control", `no-cache, no-store`);
|
|
221
|
+
}
|
|
222
|
+
event.context._isSitemap = true;
|
|
223
|
+
}
|
|
208
224
|
export async function createSitemap(event, definition, runtimeConfig) {
|
|
209
225
|
const resolvers = useNitroUrlResolvers(event);
|
|
210
226
|
const shouldStream = !!runtimeConfig.experimentalStreaming && !import.meta.prerender;
|
|
@@ -224,19 +240,6 @@ export async function createSitemap(event, definition, runtimeConfig) {
|
|
|
224
240
|
} else {
|
|
225
241
|
xml = shouldCache ? await buildSitemapXmlCached(event, definition, resolvers, runtimeConfig) : await buildSitemapXml(event, definition, resolvers, runtimeConfig);
|
|
226
242
|
}
|
|
227
|
-
|
|
228
|
-
if (runtimeConfig.cacheMaxAgeSeconds) {
|
|
229
|
-
setHeader(event, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, s-maxage=${runtimeConfig.cacheMaxAgeSeconds}, stale-while-revalidate=3600`);
|
|
230
|
-
const now = /* @__PURE__ */ new Date();
|
|
231
|
-
setHeader(event, "X-Sitemap-Generated", now.toISOString());
|
|
232
|
-
setHeader(event, "X-Sitemap-Cache-Duration", `${runtimeConfig.cacheMaxAgeSeconds}s`);
|
|
233
|
-
const expiryTime = new Date(now.getTime() + runtimeConfig.cacheMaxAgeSeconds * 1e3);
|
|
234
|
-
setHeader(event, "X-Sitemap-Cache-Expires", expiryTime.toISOString());
|
|
235
|
-
const remainingSeconds = Math.floor((expiryTime.getTime() - now.getTime()) / 1e3);
|
|
236
|
-
setHeader(event, "X-Sitemap-Cache-Remaining", `${remainingSeconds}s`);
|
|
237
|
-
} else {
|
|
238
|
-
setHeader(event, "Cache-Control", `no-cache, no-store`);
|
|
239
|
-
}
|
|
240
|
-
event.context._isSitemap = true;
|
|
243
|
+
setSitemapResponseHeaders(event, runtimeConfig);
|
|
241
244
|
return xml;
|
|
242
245
|
}
|