@nuxtjs/sitemap 8.2.3 → 8.3.1
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 +29 -45
- 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-production.d.ts +3 -3
- package/dist/runtime/server/routes/__sitemap__/debug-production.js +43 -27
- 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 +67 -0
- package/dist/runtime/server/sitemap/builder/sitemap-index.d.ts +1 -4
- package/dist/runtime/server/sitemap/builder/sitemap-index.js +15 -60
- 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 +9 -1
- package/dist/runtime/server/sitemap/builder/xml.js +37 -11
- package/dist/runtime/server/sitemap/event-handlers.d.ts +3 -3
- package/dist/runtime/server/sitemap/event-handlers.js +18 -23
- package/dist/runtime/server/sitemap/nitro.d.ts +4 -1
- package/dist/runtime/server/sitemap/nitro.js +116 -26
- 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 +26 -6
- package/dist/runtime/server/sitemap/urlset/sort.js +12 -3
- package/dist/runtime/server/sitemap/urlset/sources.js +50 -9
- 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 +43 -26
- package/dist/utils.d.mts +5 -29
- package/dist/utils.d.ts +5 -29
- package/dist/utils.mjs +3 -418
- package/package.json +28 -30
- package/dist/runtime/server/kit.d.ts +0 -2
- package/dist/runtime/server/kit.js +0 -23
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';
|
|
@@ -53,6 +54,7 @@ function setupPrerenderHandler(_options, nuxt = useNuxt()) {
|
|
|
53
54
|
}
|
|
54
55
|
nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes.filter((r) => r && !includesSitemapRoot(options.sitemapName, [r]));
|
|
55
56
|
const runtimeAssetsPath = join(nuxt.options.rootDir, "node_modules/.cache/nuxt/sitemap");
|
|
57
|
+
const localeCodes = options.autoI18n ? new Set(options.autoI18n.locales.map((l) => l.code)) : void 0;
|
|
56
58
|
nuxt.hooks.hook("nitro:config", (nitroConfig) => {
|
|
57
59
|
nitroConfig.virtual = nitroConfig.virtual || {};
|
|
58
60
|
nitroConfig.virtual["#sitemap-virtual/read-sources.mjs"] = `
|
|
@@ -103,7 +105,7 @@ export async function readSourcesFromFilesystem(filename) {
|
|
|
103
105
|
});
|
|
104
106
|
if (options.autoI18n && Object.keys(options.sitemaps).length > 1) {
|
|
105
107
|
const path = route.route;
|
|
106
|
-
const match = splitForLocales(path,
|
|
108
|
+
const match = splitForLocales(path, localeCodes);
|
|
107
109
|
const locale = match[0] || options.autoI18n.defaultLocale;
|
|
108
110
|
if (options.isI18nMapped) {
|
|
109
111
|
const { _sitemap } = options.autoI18n.locales.find((l) => l.code === locale) || { _sitemap: locale };
|
|
@@ -241,6 +243,10 @@ declare module 'nuxt/app' {
|
|
|
241
243
|
export {}
|
|
242
244
|
`;
|
|
243
245
|
}
|
|
246
|
+
}, {
|
|
247
|
+
nitro: true,
|
|
248
|
+
node: true,
|
|
249
|
+
nuxt: true
|
|
244
250
|
});
|
|
245
251
|
addTemplate({
|
|
246
252
|
filename: "types/nuxt-sitemap-virtual.d.ts",
|
|
@@ -271,25 +277,6 @@ declare module '#sitemap/content-on-url' {
|
|
|
271
277
|
});
|
|
272
278
|
}
|
|
273
279
|
|
|
274
|
-
function isValidFilter(filter) {
|
|
275
|
-
if (typeof filter === "string")
|
|
276
|
-
return true;
|
|
277
|
-
if (filter instanceof RegExp)
|
|
278
|
-
return true;
|
|
279
|
-
if (typeof filter === "object" && typeof filter.regex === "string")
|
|
280
|
-
return true;
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
function normalizeFilters(filters) {
|
|
284
|
-
return (filters || []).map((filter) => {
|
|
285
|
-
if (!isValidFilter(filter)) {
|
|
286
|
-
console.warn(`[@nuxtjs/sitemap] You have provided an invalid filter: ${filter}, ignoring.`);
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
return filter instanceof RegExp ? { regex: filter.toString() } : filter;
|
|
290
|
-
}).filter(Boolean);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
280
|
function splitPathForI18nLocales(path, autoI18n) {
|
|
294
281
|
if (typeof path !== "string")
|
|
295
282
|
return path;
|
|
@@ -461,8 +448,7 @@ function generateExtraRoutesFromNuxtConfig(nuxt = useNuxt()) {
|
|
|
461
448
|
return false;
|
|
462
449
|
return !v.redirect;
|
|
463
450
|
}).map(([k]) => k).filter(filterForValidPage);
|
|
464
|
-
|
|
465
|
-
return { routeRules, prerenderUrls };
|
|
451
|
+
return { routeRules };
|
|
466
452
|
}
|
|
467
453
|
|
|
468
454
|
const module$1 = defineNuxtModule({
|
|
@@ -501,6 +487,7 @@ const module$1 = defineNuxtModule({
|
|
|
501
487
|
// cache for 10 minutes
|
|
502
488
|
minify: false,
|
|
503
489
|
debug: false,
|
|
490
|
+
experimentalStreaming: false,
|
|
504
491
|
defaultSitemapsChunkSize: 1e3,
|
|
505
492
|
autoLastmod: false,
|
|
506
493
|
discoverImages: true,
|
|
@@ -526,8 +513,7 @@ const module$1 = defineNuxtModule({
|
|
|
526
513
|
async setup(config, nuxt) {
|
|
527
514
|
const { resolve } = createResolver(import.meta.url);
|
|
528
515
|
const { name, version } = await readPackageJSON(resolve("../package.json"));
|
|
529
|
-
const logger =
|
|
530
|
-
logger.level = config.debug || nuxt.options.debug ? 4 : 3;
|
|
516
|
+
const logger = useModuleLogger(name, config, nuxt);
|
|
531
517
|
if (config.enabled === false) {
|
|
532
518
|
logger.debug("The module is disabled, skipping setup.");
|
|
533
519
|
return;
|
|
@@ -767,13 +753,15 @@ const module$1 = defineNuxtModule({
|
|
|
767
753
|
} else if (hasRouteRuleContent) {
|
|
768
754
|
nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
|
|
769
755
|
}
|
|
770
|
-
if (config.zeroRuntime && (config.experimentalWarmUp || config.experimentalCompression))
|
|
771
|
-
logger.warn("`experimentalWarmUp` and `
|
|
756
|
+
if (config.zeroRuntime && (config.experimentalWarmUp || config.experimentalCompression || config.experimentalStreaming))
|
|
757
|
+
logger.warn("`experimentalWarmUp`, `experimentalCompression`, and `experimentalStreaming` are ignored in zeroRuntime mode.");
|
|
772
758
|
if (!config.zeroRuntime) {
|
|
773
759
|
if (config.experimentalWarmUp)
|
|
774
760
|
addServerPlugin(resolve("./runtime/server/plugins/warm-up"));
|
|
775
761
|
if (config.experimentalCompression)
|
|
776
762
|
addServerPlugin(resolve("./runtime/server/plugins/compression"));
|
|
763
|
+
if (config.experimentalStreaming || config.experimentalCompression)
|
|
764
|
+
addServerPlugin(resolve("./runtime/server/plugins/stream-transport"));
|
|
777
765
|
}
|
|
778
766
|
const isNuxtContentDocumentDriven = !!nuxt.options.content?.documentDriven || config.strictNuxtContentPaths;
|
|
779
767
|
const contentVersion = await resolveNuxtContentVersion();
|
|
@@ -1042,8 +1030,8 @@ ${onUrlEntries.join("\n")}`;
|
|
|
1042
1030
|
}
|
|
1043
1031
|
for (const sitemapName in sitemaps) {
|
|
1044
1032
|
const sitemap = sitemaps[sitemapName];
|
|
1045
|
-
sitemap.include =
|
|
1046
|
-
sitemap.exclude =
|
|
1033
|
+
sitemap.include = serializeFilters(sitemap.include || [], "@nuxtjs/sitemap");
|
|
1034
|
+
sitemap.exclude = serializeFilters(sitemap.exclude || [], "@nuxtjs/sitemap");
|
|
1047
1035
|
}
|
|
1048
1036
|
const runtimeConfig = {
|
|
1049
1037
|
isI18nMapped,
|
|
@@ -1051,6 +1039,7 @@ ${onUrlEntries.join("\n")}`;
|
|
|
1051
1039
|
isMultiSitemap: usingMultiSitemaps,
|
|
1052
1040
|
excludeAppSources: config.excludeAppSources,
|
|
1053
1041
|
cacheMaxAgeSeconds: nuxt.options.dev ? 0 : config.cacheMaxAgeSeconds,
|
|
1042
|
+
experimentalStreaming: config.experimentalStreaming,
|
|
1054
1043
|
autoLastmod: config.autoLastmod,
|
|
1055
1044
|
defaultSitemapsChunkSize: config.defaultSitemapsChunkSize,
|
|
1056
1045
|
minify: config.minify,
|
|
@@ -1148,8 +1137,8 @@ ${onUrlEntries.join("\n")}`;
|
|
|
1148
1137
|
routesNameSeparator: nuxtI18nConfig.routesNameSeparator,
|
|
1149
1138
|
normalisedLocales,
|
|
1150
1139
|
filter: {
|
|
1151
|
-
include:
|
|
1152
|
-
exclude:
|
|
1140
|
+
include: serializeFilters(config.include || [], "@nuxtjs/sitemap"),
|
|
1141
|
+
exclude: serializeFilters(config.exclude || [], "@nuxtjs/sitemap")
|
|
1153
1142
|
},
|
|
1154
1143
|
autoI18n: !!resolvedAutoI18n
|
|
1155
1144
|
});
|
|
@@ -1281,19 +1270,14 @@ export async function readSourcesFromFilesystem() {
|
|
|
1281
1270
|
}
|
|
1282
1271
|
`;
|
|
1283
1272
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
nitroConfig.virtual[`#sitemap-virtual/child-sources.mjs`] = async () => {
|
|
1293
|
-
const childSources = await generateChildSources();
|
|
1294
|
-
return `export const sources = ${JSON.stringify(childSources, null, 4)}`;
|
|
1295
|
-
};
|
|
1296
|
-
}
|
|
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
|
+
};
|
|
1297
1281
|
});
|
|
1298
1282
|
if (config.xsl === "/__sitemap__/style.xsl") {
|
|
1299
1283
|
addServerHandler({
|
|
@@ -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,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,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,67 @@
|
|
|
1
|
+
import { withQuery } from "ufo";
|
|
2
|
+
import { createChunkedXmlStream } from "../stream.js";
|
|
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
|
+
}
|
|
15
|
+
export function* renderSitemapIndexXmlChunks(sitemaps, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
16
|
+
const NL = minify ? "" : "\n";
|
|
17
|
+
const I1 = minify ? "" : " ";
|
|
18
|
+
const I2 = minify ? "" : " ";
|
|
19
|
+
yield '<?xml version="1.0" encoding="UTF-8"?>';
|
|
20
|
+
if (xsl) {
|
|
21
|
+
const relativeBaseUrl = resolveIndexXslHref(resolvers, xsl, errorInfo);
|
|
22
|
+
yield `${NL}<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`;
|
|
23
|
+
}
|
|
24
|
+
yield `${NL}<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${NL}`;
|
|
25
|
+
for (let i = 0; i < sitemaps.length; i++) {
|
|
26
|
+
const entry = sitemaps[i];
|
|
27
|
+
if (i > 0)
|
|
28
|
+
yield NL;
|
|
29
|
+
yield `${I1}<sitemap>${NL}${I2}<loc>${escapeValueForXml(entry.sitemap)}</loc>${NL}`;
|
|
30
|
+
if (entry.lastmod)
|
|
31
|
+
yield `${I2}<lastmod>${escapeValueForXml(entry.lastmod)}</lastmod>${NL}`;
|
|
32
|
+
yield `${I1}</sitemap>`;
|
|
33
|
+
}
|
|
34
|
+
yield `${NL}</sitemapindex>`;
|
|
35
|
+
if (credits)
|
|
36
|
+
yield `${NL}<!-- XML Sitemap Index generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`;
|
|
37
|
+
}
|
|
38
|
+
export function urlsToIndexXml(sitemaps, resolvers, { version, xsl, credits, minify }, errorInfo) {
|
|
39
|
+
const NL = minify ? "" : "\n";
|
|
40
|
+
const I1 = minify ? "" : " ";
|
|
41
|
+
const I2 = minify ? "" : " ";
|
|
42
|
+
let sitemapXml = "";
|
|
43
|
+
for (const entry of sitemaps) {
|
|
44
|
+
if (sitemapXml)
|
|
45
|
+
sitemapXml += NL;
|
|
46
|
+
sitemapXml += `${I1}<sitemap>${NL}${I2}<loc>${escapeValueForXml(entry.sitemap)}</loc>${NL}`;
|
|
47
|
+
if (entry.lastmod)
|
|
48
|
+
sitemapXml += `${I2}<lastmod>${escapeValueForXml(entry.lastmod)}</lastmod>${NL}`;
|
|
49
|
+
sitemapXml += `${I1}</sitemap>`;
|
|
50
|
+
}
|
|
51
|
+
const xmlParts = ['<?xml version="1.0" encoding="UTF-8"?>'];
|
|
52
|
+
if (xsl) {
|
|
53
|
+
const relativeBaseUrl = resolveIndexXslHref(resolvers, xsl, errorInfo);
|
|
54
|
+
xmlParts.push(`<?xml-stylesheet type="text/xsl" href="${escapeValueForXml(relativeBaseUrl)}"?>`);
|
|
55
|
+
}
|
|
56
|
+
xmlParts.push(
|
|
57
|
+
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
58
|
+
sitemapXml,
|
|
59
|
+
"</sitemapindex>"
|
|
60
|
+
);
|
|
61
|
+
if (credits)
|
|
62
|
+
xmlParts.push(`<!-- XML Sitemap Index generated by @nuxtjs/sitemap v${version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`);
|
|
63
|
+
return xmlParts.join(NL);
|
|
64
|
+
}
|
|
65
|
+
export function urlsToIndexXmlStream(sitemaps, resolvers, config, errorInfo) {
|
|
66
|
+
return createChunkedXmlStream(renderSitemapIndexXmlChunks(sitemaps, resolvers, config, errorInfo));
|
|
67
|
+
}
|
|
@@ -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';
|