@nuxtjs/sitemap 2.4.0 → 5.0.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/README.md +60 -593
- package/dist/client/200.html +11 -0
- package/dist/client/404.html +11 -0
- package/dist/client/_nuxt/Icon.I3NdYkjC.css +1 -0
- package/dist/client/_nuxt/Icon.wdYGFqbO.js +1 -0
- package/dist/client/_nuxt/IconCSS.6oz918NR.css +1 -0
- package/dist/client/_nuxt/IconCSS.L-uj6Ouj.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -0
- package/dist/client/_nuxt/builds/meta/c3bdccb2-d4db-4a94-9e61-39b740a57bf8.json +1 -0
- package/dist/client/_nuxt/entry.Soe9IWze.js +15 -0
- package/dist/client/_nuxt/entry.UqhvG0ao.css +1 -0
- package/dist/client/_nuxt/error-404.DkXpI38i.css +1 -0
- package/dist/client/_nuxt/error-404.uz-DxZsp.js +1 -0
- package/dist/client/_nuxt/error-500.BsNDeZpL.js +1 -0
- package/dist/client/_nuxt/error-500.SLhS9LVu.css +1 -0
- package/dist/client/_nuxt/index.lSDm5iYo.js +1 -0
- package/dist/client/index.html +11 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +350 -0
- package/dist/module.d.ts +350 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +994 -0
- package/dist/runtime/nitro/composables/asSitemapUrl.d.ts +2 -0
- package/dist/runtime/nitro/composables/asSitemapUrl.mjs +3 -0
- package/dist/runtime/nitro/composables/defineSitemapEventHandler.d.ts +3 -0
- package/dist/runtime/nitro/composables/defineSitemapEventHandler.mjs +2 -0
- package/dist/runtime/nitro/composables/getPathRobotConfigPolyfill.d.ts +4 -0
- package/dist/runtime/nitro/composables/getPathRobotConfigPolyfill.mjs +3 -0
- package/dist/runtime/nitro/kit.d.ts +3 -0
- package/dist/runtime/nitro/kit.mjs +23 -0
- package/dist/runtime/nitro/middleware/[sitemap]-sitemap.xml.d.ts +2 -0
- package/dist/runtime/nitro/middleware/[sitemap]-sitemap.xml.mjs +23 -0
- package/dist/runtime/nitro/plugins/compression.d.ts +2 -0
- package/dist/runtime/nitro/plugins/compression.mjs +8 -0
- package/dist/runtime/nitro/plugins/nuxt-content.d.ts +2 -0
- package/dist/runtime/nitro/plugins/nuxt-content.mjs +38 -0
- package/dist/runtime/nitro/plugins/warm-up.d.ts +2 -0
- package/dist/runtime/nitro/plugins/warm-up.mjs +23 -0
- package/dist/runtime/nitro/routes/__sitemap__/debug.d.ts +37 -0
- package/dist/runtime/nitro/routes/__sitemap__/debug.mjs +29 -0
- package/dist/runtime/nitro/routes/__sitemap__/nuxt-content-urls.d.ts +2 -0
- package/dist/runtime/nitro/routes/__sitemap__/nuxt-content-urls.mjs +6 -0
- package/dist/runtime/nitro/routes/sitemap.xml.d.ts +2 -0
- package/dist/runtime/nitro/routes/sitemap.xml.mjs +13 -0
- package/dist/runtime/nitro/routes/sitemap.xsl.d.ts +2 -0
- package/dist/runtime/nitro/routes/sitemap.xsl.mjs +230 -0
- package/dist/runtime/nitro/routes/sitemap_index.xml.d.ts +2 -0
- package/dist/runtime/nitro/routes/sitemap_index.xml.mjs +27 -0
- package/dist/runtime/nitro/sitemap/builder/sitemap-index.d.ts +2 -0
- package/dist/runtime/nitro/sitemap/builder/sitemap-index.mjs +86 -0
- package/dist/runtime/nitro/sitemap/builder/sitemap.d.ts +2 -0
- package/dist/runtime/nitro/sitemap/builder/sitemap.mjs +107 -0
- package/dist/runtime/nitro/sitemap/builder/xml.d.ts +4 -0
- package/dist/runtime/nitro/sitemap/builder/xml.mjs +83 -0
- package/dist/runtime/nitro/sitemap/nitro.d.ts +4 -0
- package/dist/runtime/nitro/sitemap/nitro.mjs +36 -0
- package/dist/runtime/nitro/sitemap/urlset/filter.d.ts +5 -0
- package/dist/runtime/nitro/sitemap/urlset/filter.mjs +50 -0
- package/dist/runtime/nitro/sitemap/urlset/i18n.d.ts +8 -0
- package/dist/runtime/nitro/sitemap/urlset/i18n.mjs +128 -0
- package/dist/runtime/nitro/sitemap/urlset/normalise.d.ts +3 -0
- package/dist/runtime/nitro/sitemap/urlset/normalise.mjs +77 -0
- package/dist/runtime/nitro/sitemap/urlset/sort.d.ts +2 -0
- package/dist/runtime/nitro/sitemap/urlset/sort.mjs +19 -0
- package/dist/runtime/nitro/sitemap/urlset/sources.d.ts +5 -0
- package/dist/runtime/nitro/sitemap/urlset/sources.mjs +82 -0
- package/dist/runtime/nitro/tsconfig.json +3 -0
- package/dist/runtime/nitro/utils.d.ts +4 -0
- package/dist/runtime/nitro/utils.mjs +13 -0
- package/dist/runtime/types.d.ts +355 -0
- package/dist/runtime/types.mjs +0 -0
- package/dist/runtime/utils-pure.d.ts +7 -0
- package/dist/runtime/utils-pure.mjs +32 -0
- package/dist/types.d.mts +18 -0
- package/dist/types.d.ts +18 -0
- package/package.json +79 -75
- package/CHANGELOG.md +0 -268
- package/LICENSE +0 -21
- package/lib/builder.js +0 -172
- package/lib/cache.js +0 -95
- package/lib/generator.js +0 -113
- package/lib/logger.js +0 -19
- package/lib/middleware.js +0 -195
- package/lib/module.js +0 -72
- package/lib/options.js +0 -135
- package/lib/routes.js +0 -55
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { defu } from "defu";
|
|
2
|
+
import { resolveSitePath } from "site-config-stack/urls";
|
|
3
|
+
import { parseURL, withHttps } from "ufo";
|
|
4
|
+
import { normaliseSitemapUrls } from "../urlset/normalise.mjs";
|
|
5
|
+
import { childSitemapSources, globalSitemapSources, resolveSitemapSources } from "../urlset/sources.mjs";
|
|
6
|
+
import { filterSitemapUrls } from "../urlset/filter.mjs";
|
|
7
|
+
import { applyI18nEnhancements, normaliseI18nSources } from "../urlset/i18n.mjs";
|
|
8
|
+
import { sortSitemapUrls } from "../urlset/sort.mjs";
|
|
9
|
+
import { splitForLocales } from "../../utils.mjs";
|
|
10
|
+
import { createNitroRouteRuleMatcher } from "../../kit.mjs";
|
|
11
|
+
import { handleEntry, wrapSitemapXml } from "./xml.mjs";
|
|
12
|
+
import { useNitroApp } from "#imports";
|
|
13
|
+
export async function buildSitemap(sitemap, resolvers, runtimeConfig) {
|
|
14
|
+
const {
|
|
15
|
+
sitemaps,
|
|
16
|
+
// enhancing
|
|
17
|
+
autoLastmod,
|
|
18
|
+
autoI18n,
|
|
19
|
+
isI18nMapped,
|
|
20
|
+
isMultiSitemap,
|
|
21
|
+
// sorting
|
|
22
|
+
sortEntries,
|
|
23
|
+
// chunking
|
|
24
|
+
defaultSitemapsChunkSize,
|
|
25
|
+
// xls
|
|
26
|
+
version,
|
|
27
|
+
xsl,
|
|
28
|
+
credits
|
|
29
|
+
} = runtimeConfig;
|
|
30
|
+
const isChunking = typeof sitemaps.chunks !== "undefined" && !Number.isNaN(Number(sitemap.sitemapName));
|
|
31
|
+
function maybeSort(urls2) {
|
|
32
|
+
return sortEntries ? sortSitemapUrls(urls2) : urls2;
|
|
33
|
+
}
|
|
34
|
+
function maybeSlice(urls2) {
|
|
35
|
+
if (isChunking && defaultSitemapsChunkSize) {
|
|
36
|
+
const chunk = Number(sitemap.sitemapName);
|
|
37
|
+
return urls2.slice(chunk * defaultSitemapsChunkSize, (chunk + 1) * defaultSitemapsChunkSize);
|
|
38
|
+
}
|
|
39
|
+
return urls2;
|
|
40
|
+
}
|
|
41
|
+
if (autoI18n?.differentDomains) {
|
|
42
|
+
const domain = autoI18n.locales.find((e) => [e.iso, e.code].includes(sitemap.sitemapName))?.domain;
|
|
43
|
+
if (domain) {
|
|
44
|
+
const _tester = resolvers.canonicalUrlResolver;
|
|
45
|
+
resolvers.canonicalUrlResolver = (path) => resolveSitePath(path, {
|
|
46
|
+
absolute: true,
|
|
47
|
+
withBase: false,
|
|
48
|
+
siteUrl: withHttps(domain),
|
|
49
|
+
trailingSlash: !_tester("/test/").endsWith("/"),
|
|
50
|
+
base: "/"
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const sources = sitemap.includeAppSources ? await globalSitemapSources() : [];
|
|
55
|
+
sources.push(...await childSitemapSources(sitemap));
|
|
56
|
+
let resolvedSources = await resolveSitemapSources(sources);
|
|
57
|
+
if (autoI18n)
|
|
58
|
+
resolvedSources = normaliseI18nSources(resolvedSources, { autoI18n, isI18nMapped });
|
|
59
|
+
const normalisedUrls = normaliseSitemapUrls(resolvedSources.map((e) => e.urls).flat(), resolvers);
|
|
60
|
+
const defaults = { ...sitemap.defaults || {} };
|
|
61
|
+
if (autoLastmod && defaults?.lastmod)
|
|
62
|
+
defaults.lastmod = /* @__PURE__ */ new Date();
|
|
63
|
+
const routeRuleMatcher = createNitroRouteRuleMatcher();
|
|
64
|
+
let enhancedUrls = normalisedUrls.map((e) => defu(e, sitemap.defaults)).map((e) => {
|
|
65
|
+
const path = parseURL(e.loc).pathname;
|
|
66
|
+
let routeRules = routeRuleMatcher(path);
|
|
67
|
+
if (autoI18n?.locales && autoI18n?.strategy !== "no_prefix") {
|
|
68
|
+
const match = splitForLocales(path, autoI18n.locales.map((l) => l.code));
|
|
69
|
+
const pathWithoutPrefix = match[1];
|
|
70
|
+
if (pathWithoutPrefix && pathWithoutPrefix !== path)
|
|
71
|
+
routeRules = defu(routeRules, routeRuleMatcher(pathWithoutPrefix));
|
|
72
|
+
}
|
|
73
|
+
if (routeRules.sitemap === false)
|
|
74
|
+
return false;
|
|
75
|
+
if (typeof routeRules.index !== "undefined" && !routeRules.index)
|
|
76
|
+
return false;
|
|
77
|
+
const hasRobotsDisabled = Object.entries(routeRules.headers || {}).some(([name, value]) => name.toLowerCase() === "x-robots-tag" && value.toLowerCase() === "noindex");
|
|
78
|
+
if (routeRules.redirect || hasRobotsDisabled)
|
|
79
|
+
return false;
|
|
80
|
+
return routeRules.sitemap ? defu(e, routeRules.sitemap) : e;
|
|
81
|
+
}).filter(Boolean);
|
|
82
|
+
if (autoI18n?.locales)
|
|
83
|
+
enhancedUrls = applyI18nEnhancements(enhancedUrls, { isI18nMapped, autoI18n, sitemapName: sitemap.sitemapName });
|
|
84
|
+
const filteredUrls = filterSitemapUrls(enhancedUrls, { event: resolvers.event, isMultiSitemap, autoI18n, ...sitemap });
|
|
85
|
+
const sortedUrls = maybeSort(filteredUrls);
|
|
86
|
+
const slicedUrls = maybeSlice(sortedUrls);
|
|
87
|
+
const nitro = useNitroApp();
|
|
88
|
+
const ctx = {
|
|
89
|
+
urls: slicedUrls,
|
|
90
|
+
sitemapName: sitemap.sitemapName
|
|
91
|
+
};
|
|
92
|
+
await nitro.hooks.callHook("sitemap:resolved", ctx);
|
|
93
|
+
const urls = maybeSort(normaliseSitemapUrls(ctx.urls, resolvers));
|
|
94
|
+
const urlset = urls.map((e) => {
|
|
95
|
+
const keys = Object.keys(e).filter((k) => !k.startsWith("_"));
|
|
96
|
+
return [
|
|
97
|
+
" <url>",
|
|
98
|
+
keys.map((k) => handleEntry(k, e)).filter(Boolean).join("\n"),
|
|
99
|
+
" </url>"
|
|
100
|
+
].join("\n");
|
|
101
|
+
});
|
|
102
|
+
return wrapSitemapXml([
|
|
103
|
+
'<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
|
104
|
+
urlset.join("\n"),
|
|
105
|
+
"</urlset>"
|
|
106
|
+
], resolvers, { version, xsl, credits });
|
|
107
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ModuleRuntimeConfig, NitroUrlResolvers } from '../../../types';
|
|
2
|
+
export declare function handleEntry(k: string, e: Record<string, any> | (string | Record<string, any>)[]): string | false;
|
|
3
|
+
export declare function wrapSitemapXml(input: string[], resolvers: NitroUrlResolvers, options: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits'>): string;
|
|
4
|
+
export declare function escapeValueForXml(value: boolean | string | number): string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
function resolveKey(k) {
|
|
2
|
+
switch (k) {
|
|
3
|
+
case "images":
|
|
4
|
+
return "image";
|
|
5
|
+
case "videos":
|
|
6
|
+
return "video";
|
|
7
|
+
case "news":
|
|
8
|
+
return "news";
|
|
9
|
+
default:
|
|
10
|
+
return k;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function handleObject(key, obj) {
|
|
14
|
+
return [
|
|
15
|
+
` <${key}:${key}>`,
|
|
16
|
+
...Object.entries(obj).map(([sk, sv]) => {
|
|
17
|
+
if (key === "video" && Array.isArray(sv)) {
|
|
18
|
+
return sv.map((v) => {
|
|
19
|
+
if (typeof v === "string") {
|
|
20
|
+
return [
|
|
21
|
+
` `,
|
|
22
|
+
`<${key}:${sk}>`,
|
|
23
|
+
escapeValueForXml(v),
|
|
24
|
+
`</${key}:${sk}>`
|
|
25
|
+
].join("");
|
|
26
|
+
}
|
|
27
|
+
const attributes = Object.entries(v).filter(([ssk]) => ssk !== sk).map(([ssk, ssv]) => `${ssk}="${escapeValueForXml(ssv)}"`).join(" ");
|
|
28
|
+
return [
|
|
29
|
+
` <${key}:${sk} ${attributes}>`,
|
|
30
|
+
// value is the same sk
|
|
31
|
+
v[sk],
|
|
32
|
+
`</${key}:${sk}>`
|
|
33
|
+
].join("");
|
|
34
|
+
}).join("\n");
|
|
35
|
+
}
|
|
36
|
+
if (typeof sv === "object") {
|
|
37
|
+
if (key === "video") {
|
|
38
|
+
const attributes = Object.entries(sv).filter(([ssk]) => ssk !== sk).map(([ssk, ssv]) => `${ssk}="${escapeValueForXml(ssv)}"`).join(" ");
|
|
39
|
+
return [
|
|
40
|
+
` <${key}:${sk} ${attributes}>`,
|
|
41
|
+
// value is the same sk
|
|
42
|
+
sv[sk],
|
|
43
|
+
`</${key}:${sk}>`
|
|
44
|
+
].join("");
|
|
45
|
+
}
|
|
46
|
+
return [
|
|
47
|
+
` <${key}:${sk}>`,
|
|
48
|
+
...Object.entries(sv).map(([ssk, ssv]) => ` <${key}:${ssk}>${escapeValueForXml(ssv)}</${key}:${ssk}>`),
|
|
49
|
+
` </${key}:${sk}>`
|
|
50
|
+
].join("\n");
|
|
51
|
+
}
|
|
52
|
+
return ` <${key}:${sk}>${escapeValueForXml(sv)}</${key}:${sk}>`;
|
|
53
|
+
}),
|
|
54
|
+
` </${key}:${key}>`
|
|
55
|
+
].join("\n");
|
|
56
|
+
}
|
|
57
|
+
function handleArray(key, arr) {
|
|
58
|
+
if (arr.length === 0)
|
|
59
|
+
return false;
|
|
60
|
+
key = resolveKey(key);
|
|
61
|
+
if (key === "alternatives") {
|
|
62
|
+
return arr.map((obj) => [
|
|
63
|
+
` <xhtml:link rel="alternate" ${Object.entries(obj).map(([sk, sv]) => `${sk}="${escapeValueForXml(sv)}"`).join(" ")} />`
|
|
64
|
+
].join("\n")).join("\n");
|
|
65
|
+
}
|
|
66
|
+
return arr.map((obj) => handleObject(key, obj)).join("\n");
|
|
67
|
+
}
|
|
68
|
+
export function handleEntry(k, e) {
|
|
69
|
+
return Array.isArray(e[k]) ? handleArray(k, e[k]) : typeof e[k] === "object" ? handleObject(k, e[k]) : ` <${k}>${escapeValueForXml(e[k])}</${k}>`;
|
|
70
|
+
}
|
|
71
|
+
export function wrapSitemapXml(input, resolvers, options) {
|
|
72
|
+
const xsl = options.xsl ? resolvers.relativeBaseUrlResolver(options.xsl) : false;
|
|
73
|
+
const credits = options.credits;
|
|
74
|
+
input.unshift(`<?xml version="1.0" encoding="UTF-8"?>${xsl ? `<?xml-stylesheet type="text/xsl" href="${xsl}"?>` : ""}`);
|
|
75
|
+
if (credits)
|
|
76
|
+
input.push(`<!-- XML Sitemap generated by @nuxtjs/sitemap v${options.version} at ${(/* @__PURE__ */ new Date()).toISOString()} -->`);
|
|
77
|
+
return input.join("\n");
|
|
78
|
+
}
|
|
79
|
+
export function escapeValueForXml(value) {
|
|
80
|
+
if (value === true || value === false)
|
|
81
|
+
return value ? "yes" : "no";
|
|
82
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
83
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type H3Event } from 'h3';
|
|
2
|
+
import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapDefinition } from '../../types';
|
|
3
|
+
export declare function useNitroUrlResolvers(e: H3Event): NitroUrlResolvers;
|
|
4
|
+
export declare function createSitemap(e: H3Event, definition: SitemapDefinition, runtimeConfig: ModuleRuntimeConfig): Promise<string>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getQuery, setHeader } from "h3";
|
|
2
|
+
import { fixSlashes } from "site-config-stack/urls";
|
|
3
|
+
import { buildSitemap } from "./builder/sitemap.mjs";
|
|
4
|
+
import { buildSitemapIndex } from "./builder/sitemap-index.mjs";
|
|
5
|
+
import { createSitePathResolver, useNitroApp, useSiteConfig } from "#imports";
|
|
6
|
+
export function useNitroUrlResolvers(e) {
|
|
7
|
+
const canonicalQuery = getQuery(e).canonical;
|
|
8
|
+
const isShowingCanonical = typeof canonicalQuery !== "undefined" && canonicalQuery !== "false";
|
|
9
|
+
const siteConfig = useSiteConfig(e);
|
|
10
|
+
return {
|
|
11
|
+
event: e,
|
|
12
|
+
fixSlashes: (path) => fixSlashes(siteConfig.trailingSlash, path),
|
|
13
|
+
// we need these as they depend on the nitro event
|
|
14
|
+
canonicalUrlResolver: createSitePathResolver(e, {
|
|
15
|
+
canonical: isShowingCanonical || !process.dev,
|
|
16
|
+
absolute: true,
|
|
17
|
+
withBase: true
|
|
18
|
+
}),
|
|
19
|
+
relativeBaseUrlResolver: createSitePathResolver(e, { absolute: false, withBase: true })
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export async function createSitemap(e, definition, runtimeConfig) {
|
|
23
|
+
const { sitemapName } = definition;
|
|
24
|
+
const nitro = useNitroApp();
|
|
25
|
+
let sitemap = await (definition.sitemapName === "index" ? buildSitemapIndex(useNitroUrlResolvers(e), runtimeConfig) : buildSitemap(definition, useNitroUrlResolvers(e), runtimeConfig));
|
|
26
|
+
const ctx = { sitemap, sitemapName };
|
|
27
|
+
await nitro.hooks.callHook("sitemap:output", ctx);
|
|
28
|
+
sitemap = ctx.sitemap;
|
|
29
|
+
setHeader(e, "Content-Type", "text/xml; charset=UTF-8");
|
|
30
|
+
if (runtimeConfig.cacheMaxAgeSeconds)
|
|
31
|
+
setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, must-revalidate`);
|
|
32
|
+
else
|
|
33
|
+
setHeader(e, "Cache-Control", `no-cache, no-store`);
|
|
34
|
+
e.context._isSitemap = true;
|
|
35
|
+
return sitemap;
|
|
36
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import type { ModuleRuntimeConfig, ResolvedSitemapUrl } from '../../../types';
|
|
3
|
+
export declare function filterSitemapUrls(_urls: ResolvedSitemapUrl[], options: Pick<ModuleRuntimeConfig, 'autoI18n' | 'isMultiSitemap'> & Pick<ModuleRuntimeConfig['sitemaps'][string], 'sitemapName' | 'include' | 'exclude'> & {
|
|
4
|
+
event: H3Event;
|
|
5
|
+
}): ResolvedSitemapUrl[];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseURL } from "ufo";
|
|
2
|
+
import { createRouter, toRouteMatcher } from "radix3";
|
|
3
|
+
import { getPathRobotConfig } from "#imports";
|
|
4
|
+
function createFilter(options = {}) {
|
|
5
|
+
const include = options.include || [];
|
|
6
|
+
const exclude = options.exclude || [];
|
|
7
|
+
if (include.length === 0 && exclude.length === 0)
|
|
8
|
+
return () => true;
|
|
9
|
+
return function(path) {
|
|
10
|
+
for (const v of [{ rules: exclude, result: false }, { rules: include, result: true }]) {
|
|
11
|
+
const regexRules = v.rules.filter((r) => r instanceof RegExp);
|
|
12
|
+
if (regexRules.some((r) => r.test(path)))
|
|
13
|
+
return v.result;
|
|
14
|
+
const stringRules = v.rules.filter((r) => typeof r === "string");
|
|
15
|
+
if (stringRules.length > 0) {
|
|
16
|
+
const routes = {};
|
|
17
|
+
for (const r of stringRules) {
|
|
18
|
+
if (r === path)
|
|
19
|
+
return v.result;
|
|
20
|
+
routes[r] = true;
|
|
21
|
+
}
|
|
22
|
+
const routeRulesMatcher = toRouteMatcher(createRouter({ routes, strictTrailingSlash: false }));
|
|
23
|
+
if (routeRulesMatcher.matchAll(path).length > 0)
|
|
24
|
+
return Boolean(v.result);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return include.length === 0;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function filterSitemapUrls(_urls, options) {
|
|
31
|
+
const urlFilter = createFilter({
|
|
32
|
+
include: options.include,
|
|
33
|
+
exclude: options.exclude
|
|
34
|
+
});
|
|
35
|
+
return _urls.filter((e) => {
|
|
36
|
+
let path = e.loc;
|
|
37
|
+
try {
|
|
38
|
+
path = parseURL(e.loc).pathname;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (!urlFilter(path))
|
|
43
|
+
return false;
|
|
44
|
+
if (options.isMultiSitemap && e._sitemap && options.sitemapName)
|
|
45
|
+
return e._sitemap === options.sitemapName;
|
|
46
|
+
if (!getPathRobotConfig(e, { path, skipSiteIndexable: true }).indexable)
|
|
47
|
+
return false;
|
|
48
|
+
return true;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ModuleRuntimeConfig, ResolvedSitemapUrl, SitemapSourceResolved } from '../../../types';
|
|
2
|
+
export declare function normaliseI18nSources(sources: SitemapSourceResolved[], { autoI18n, isI18nMapped }: {
|
|
3
|
+
autoI18n: ModuleRuntimeConfig['autoI18n'];
|
|
4
|
+
isI18nMapped: boolean;
|
|
5
|
+
}): SitemapSourceResolved[];
|
|
6
|
+
export declare function applyI18nEnhancements(_urls: ResolvedSitemapUrl[], options: Pick<Required<ModuleRuntimeConfig>, 'autoI18n' | 'isI18nMapped'> & {
|
|
7
|
+
sitemapName: string;
|
|
8
|
+
}): ResolvedSitemapUrl[];
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { joinURL, parseURL, withHttps, withLeadingSlash } from "ufo";
|
|
2
|
+
import { splitForLocales } from "../../../utils-pure.mjs";
|
|
3
|
+
export function normaliseI18nSources(sources, { autoI18n, isI18nMapped }) {
|
|
4
|
+
if (autoI18n && isI18nMapped) {
|
|
5
|
+
return sources.map((s) => {
|
|
6
|
+
const urls = (s.urls || []).map((_url) => {
|
|
7
|
+
const url = typeof _url === "string" ? { loc: _url } : _url;
|
|
8
|
+
url.loc = url.loc || url.url;
|
|
9
|
+
url.loc = withLeadingSlash(url.loc);
|
|
10
|
+
return url;
|
|
11
|
+
});
|
|
12
|
+
s.urls = urls.map((url) => {
|
|
13
|
+
if (url._sitemap || url._i18nTransform)
|
|
14
|
+
return url;
|
|
15
|
+
if (url.loc) {
|
|
16
|
+
const match = splitForLocales(url.loc, autoI18n.locales.map((l) => l.code));
|
|
17
|
+
const localeCode = match[0] || autoI18n.defaultLocale;
|
|
18
|
+
const pathWithoutPrefix = match[1];
|
|
19
|
+
const locale = autoI18n.locales.find((e) => e.code === localeCode);
|
|
20
|
+
if (locale) {
|
|
21
|
+
if (!url.alternatives) {
|
|
22
|
+
const alternatives = urls.map((u) => {
|
|
23
|
+
if (u._sitemap || u._i18nTransform)
|
|
24
|
+
return false;
|
|
25
|
+
if (u?.loc) {
|
|
26
|
+
const [_localeCode, _pathWithoutPrefix] = splitForLocales(u.loc, autoI18n.locales.map((l) => l.code));
|
|
27
|
+
if (pathWithoutPrefix === _pathWithoutPrefix) {
|
|
28
|
+
const entries = [];
|
|
29
|
+
if (_localeCode === autoI18n.defaultLocale) {
|
|
30
|
+
entries.push({
|
|
31
|
+
href: u.loc,
|
|
32
|
+
hreflang: "x-default"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
entries.push({
|
|
36
|
+
href: u.loc,
|
|
37
|
+
hreflang: _localeCode || autoI18n.defaultLocale
|
|
38
|
+
});
|
|
39
|
+
return entries;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}).flat().filter(Boolean);
|
|
44
|
+
if (alternatives.length)
|
|
45
|
+
url.alternatives = alternatives;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
_sitemap: locale.iso || locale.code,
|
|
49
|
+
...url
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return url;
|
|
54
|
+
});
|
|
55
|
+
return s;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return sources;
|
|
59
|
+
}
|
|
60
|
+
export function applyI18nEnhancements(_urls, options) {
|
|
61
|
+
const { autoI18n } = options;
|
|
62
|
+
return _urls.map((e) => {
|
|
63
|
+
if (!e._i18nTransform)
|
|
64
|
+
return e;
|
|
65
|
+
delete e._i18nTransform;
|
|
66
|
+
const path = withLeadingSlash(parseURL(e.loc).pathname);
|
|
67
|
+
const match = splitForLocales(path, autoI18n.locales.map((l) => l.code));
|
|
68
|
+
let pathWithoutLocale = path;
|
|
69
|
+
let locale;
|
|
70
|
+
if (match[0]) {
|
|
71
|
+
pathWithoutLocale = match[1] || "/";
|
|
72
|
+
locale = match[0];
|
|
73
|
+
}
|
|
74
|
+
if (locale && import.meta.dev) {
|
|
75
|
+
console.warn("You're providing a locale in the url, but the url is marked as inheritI18n. This will cause issues with the sitemap. Please remove the locale from the url.");
|
|
76
|
+
return e;
|
|
77
|
+
}
|
|
78
|
+
if (autoI18n.differentDomains) {
|
|
79
|
+
return {
|
|
80
|
+
// will force it to pass filter
|
|
81
|
+
_sitemap: options.sitemapName,
|
|
82
|
+
...e,
|
|
83
|
+
alternatives: [
|
|
84
|
+
{
|
|
85
|
+
// apply default locale domain
|
|
86
|
+
...autoI18n.locales.find((l) => [l.code, l.iso].includes(autoI18n.defaultLocale)),
|
|
87
|
+
code: "x-default"
|
|
88
|
+
},
|
|
89
|
+
...autoI18n.locales.filter((l) => !!l.domain)
|
|
90
|
+
].map((locale2) => {
|
|
91
|
+
return {
|
|
92
|
+
hreflang: locale2.iso || locale2.code,
|
|
93
|
+
href: joinURL(withHttps(locale2.domain), pathWithoutLocale)
|
|
94
|
+
};
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return autoI18n.locales.map((l) => {
|
|
99
|
+
let loc = joinURL(`/${l.code}`, pathWithoutLocale);
|
|
100
|
+
if (autoI18n.differentDomains || ["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy) && l.code === autoI18n.defaultLocale)
|
|
101
|
+
loc = pathWithoutLocale;
|
|
102
|
+
return {
|
|
103
|
+
_sitemap: options.isI18nMapped ? l.iso || l.code : void 0,
|
|
104
|
+
...e,
|
|
105
|
+
loc,
|
|
106
|
+
alternatives: [{ code: "x-default" }, ...autoI18n.locales].map((locale2) => {
|
|
107
|
+
const code = locale2.code === "x-default" ? autoI18n.defaultLocale : locale2.code;
|
|
108
|
+
const isDefault = locale2.code === "x-default" || locale2.code === autoI18n.defaultLocale;
|
|
109
|
+
let href = "";
|
|
110
|
+
if (autoI18n.strategy === "prefix") {
|
|
111
|
+
href = joinURL("/", code, pathWithoutLocale);
|
|
112
|
+
} else if (["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy)) {
|
|
113
|
+
if (isDefault) {
|
|
114
|
+
href = pathWithoutLocale;
|
|
115
|
+
} else {
|
|
116
|
+
href = joinURL("/", code, pathWithoutLocale);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const hreflang = locale2.iso || locale2.code;
|
|
120
|
+
return {
|
|
121
|
+
hreflang,
|
|
122
|
+
href
|
|
123
|
+
};
|
|
124
|
+
})
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}).flat();
|
|
128
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { NitroUrlResolvers, ResolvedSitemapUrl, SitemapUrlInput } from '../../../types';
|
|
2
|
+
export declare function normaliseSitemapUrls(data: SitemapUrlInput[], resolvers: NitroUrlResolvers): ResolvedSitemapUrl[];
|
|
3
|
+
export declare function normaliseDate(date: string | Date): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { hasProtocol } from "ufo";
|
|
2
|
+
import { fixSlashes } from "site-config-stack/urls";
|
|
3
|
+
import { mergeOnKey } from "../../../utils-pure.mjs";
|
|
4
|
+
function resolve(s, resolvers) {
|
|
5
|
+
if (typeof s === "undefined")
|
|
6
|
+
return s;
|
|
7
|
+
s = typeof s === "string" ? s : s.toString();
|
|
8
|
+
if (hasProtocol(s, { acceptRelative: true, strict: false }))
|
|
9
|
+
return resolvers.fixSlashes(s);
|
|
10
|
+
return resolvers.canonicalUrlResolver(s);
|
|
11
|
+
}
|
|
12
|
+
export function normaliseSitemapUrls(data, resolvers) {
|
|
13
|
+
const entries = data.map((e) => typeof e === "string" ? { loc: e } : e).map((e) => {
|
|
14
|
+
e = { ...e };
|
|
15
|
+
if (e.url) {
|
|
16
|
+
e.loc = e.url;
|
|
17
|
+
delete e.url;
|
|
18
|
+
}
|
|
19
|
+
e.loc = fixSlashes(false, e.loc);
|
|
20
|
+
return e;
|
|
21
|
+
}).filter(Boolean);
|
|
22
|
+
function normaliseEntry(e) {
|
|
23
|
+
if (e.lastmod) {
|
|
24
|
+
const date = normaliseDate(e.lastmod);
|
|
25
|
+
if (date)
|
|
26
|
+
e.lastmod = date;
|
|
27
|
+
else
|
|
28
|
+
delete e.lastmod;
|
|
29
|
+
}
|
|
30
|
+
if (!e.lastmod)
|
|
31
|
+
delete e.lastmod;
|
|
32
|
+
e.loc = resolve(e.loc, resolvers);
|
|
33
|
+
if (e.alternatives) {
|
|
34
|
+
e.alternatives = mergeOnKey(e.alternatives.map((e2) => {
|
|
35
|
+
const a = { ...e2 };
|
|
36
|
+
if (typeof a.href === "string")
|
|
37
|
+
a.href = resolve(a.href, resolvers);
|
|
38
|
+
else if (typeof a.href === "object" && a.href)
|
|
39
|
+
a.href = resolve(a.href.href, resolvers);
|
|
40
|
+
return a;
|
|
41
|
+
}), "hreflang");
|
|
42
|
+
}
|
|
43
|
+
if (e.images) {
|
|
44
|
+
e.images = mergeOnKey(e.images.map((i) => {
|
|
45
|
+
i = { ...i };
|
|
46
|
+
i.loc = resolve(i.loc, resolvers);
|
|
47
|
+
return i;
|
|
48
|
+
}), "loc");
|
|
49
|
+
}
|
|
50
|
+
if (e.videos) {
|
|
51
|
+
e.videos = e.videos.map((v) => {
|
|
52
|
+
v = { ...v };
|
|
53
|
+
if (v.content_loc)
|
|
54
|
+
v.content_loc = resolve(v.content_loc, resolvers);
|
|
55
|
+
return v;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return e;
|
|
59
|
+
}
|
|
60
|
+
return mergeOnKey(
|
|
61
|
+
entries.map(normaliseEntry).map((e) => ({ ...e, _key: `${e._sitemap || ""}${e.loc}` })),
|
|
62
|
+
"_key"
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
export function normaliseDate(d) {
|
|
66
|
+
if (typeof d === "string") {
|
|
67
|
+
d = d.replace("Z", "");
|
|
68
|
+
d = d.replace(/\.\d+$/, "");
|
|
69
|
+
if (d.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/) || d.match(/^\d{4}-\d{2}-\d{2}$/))
|
|
70
|
+
return d;
|
|
71
|
+
d = new Date(d);
|
|
72
|
+
if (Number.isNaN(d.getTime()))
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const z = (n) => `0${n}`.slice(-2);
|
|
76
|
+
return `${d.getUTCFullYear()}-${z(d.getUTCMonth() + 1)}-${z(d.getUTCDate())}T${z(d.getUTCHours())}:${z(d.getUTCMinutes())}:${z(d.getUTCSeconds())}+00:00`;
|
|
77
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function sortSitemapUrls(urls) {
|
|
2
|
+
return urls.sort(
|
|
3
|
+
(a, b) => {
|
|
4
|
+
const aLoc = typeof a === "string" ? a : a.loc;
|
|
5
|
+
const bLoc = typeof b === "string" ? b : b.loc;
|
|
6
|
+
return aLoc.localeCompare(bLoc, void 0, { numeric: true });
|
|
7
|
+
}
|
|
8
|
+
).sort((a, b) => {
|
|
9
|
+
const aLoc = (typeof a === "string" ? a : a.loc) || "";
|
|
10
|
+
const bLoc = (typeof b === "string" ? b : b.loc) || "";
|
|
11
|
+
const aSegments = aLoc.split("/").length;
|
|
12
|
+
const bSegments = bLoc.split("/").length;
|
|
13
|
+
if (aSegments > bSegments)
|
|
14
|
+
return 1;
|
|
15
|
+
if (aSegments < bSegments)
|
|
16
|
+
return -1;
|
|
17
|
+
return 0;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ModuleRuntimeConfig, SitemapSourceBase, SitemapSourceResolved } from '../../../types';
|
|
2
|
+
export declare function fetchDataSource(input: SitemapSourceBase | SitemapSourceResolved): Promise<SitemapSourceResolved>;
|
|
3
|
+
export declare function globalSitemapSources(): Promise<(SitemapSourceBase | SitemapSourceResolved)[]>;
|
|
4
|
+
export declare function childSitemapSources(definition: ModuleRuntimeConfig['sitemaps'][string]): Promise<(SitemapSourceBase | SitemapSourceResolved)[]>;
|
|
5
|
+
export declare function resolveSitemapSources(sources: (SitemapSourceBase | SitemapSourceResolved)[]): Promise<SitemapSourceResolved[]>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export async function fetchDataSource(input) {
|
|
2
|
+
const context = typeof input.context === "string" ? { name: input.context } : input.context || { name: "fetch" };
|
|
3
|
+
context.tips = context.tips || [];
|
|
4
|
+
const url = typeof input.fetch === "string" ? input.fetch : input.fetch[0];
|
|
5
|
+
const options = typeof input.fetch === "string" ? {} : input.fetch[1];
|
|
6
|
+
const start = Date.now();
|
|
7
|
+
const timeout = options.timeout || 5e3;
|
|
8
|
+
const timeoutController = new AbortController();
|
|
9
|
+
const abortRequestTimeout = setTimeout(() => timeoutController.abort(), timeout);
|
|
10
|
+
let isHtmlResponse = false;
|
|
11
|
+
try {
|
|
12
|
+
const urls = await globalThis.$fetch(url, {
|
|
13
|
+
responseType: "json",
|
|
14
|
+
signal: timeoutController.signal,
|
|
15
|
+
headers: {
|
|
16
|
+
Accept: "application/json"
|
|
17
|
+
},
|
|
18
|
+
// @ts-expect-error untyped
|
|
19
|
+
onResponse({ response }) {
|
|
20
|
+
if (typeof response._data === "string" && response._data.startsWith("<!DOCTYPE html>"))
|
|
21
|
+
isHtmlResponse = true;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const timeTakenMs = Date.now() - start;
|
|
25
|
+
if (isHtmlResponse) {
|
|
26
|
+
context.tips.push("This is usually because the URL isn't correct or is throwing an error. Please check the URL");
|
|
27
|
+
return {
|
|
28
|
+
...input,
|
|
29
|
+
context,
|
|
30
|
+
urls: [],
|
|
31
|
+
timeTakenMs,
|
|
32
|
+
error: "Received HTML response instead of JSON"
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...input,
|
|
37
|
+
context,
|
|
38
|
+
timeTakenMs,
|
|
39
|
+
urls
|
|
40
|
+
};
|
|
41
|
+
} catch (_err) {
|
|
42
|
+
const error = _err;
|
|
43
|
+
if (error.message.includes("This operation was aborted"))
|
|
44
|
+
context.tips.push("The request has taken too long. Make sure app sources respond within 5 seconds or adjust the timeout fetch option.");
|
|
45
|
+
else
|
|
46
|
+
context.tips.push(`Response returned a status of ${error.response?.status || "unknown"}.`);
|
|
47
|
+
console.error("[@nuxtjs/sitemap] Failed to fetch source.", { url, error });
|
|
48
|
+
return {
|
|
49
|
+
...input,
|
|
50
|
+
context,
|
|
51
|
+
urls: [],
|
|
52
|
+
error: error.message
|
|
53
|
+
};
|
|
54
|
+
} finally {
|
|
55
|
+
abortRequestTimeout && clearTimeout(abortRequestTimeout);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function globalSitemapSources() {
|
|
59
|
+
return import("#sitemap/global-sources.mjs").then((m) => m.sources);
|
|
60
|
+
}
|
|
61
|
+
export function childSitemapSources(definition) {
|
|
62
|
+
return definition?._hasSourceChunk ? import("#sitemap/child-sources.mjs").then((m) => m.sources[definition.sitemapName] || []) : Promise.resolve([]);
|
|
63
|
+
}
|
|
64
|
+
export async function resolveSitemapSources(sources) {
|
|
65
|
+
return (await Promise.all(
|
|
66
|
+
sources.map((source) => {
|
|
67
|
+
if (typeof source === "object" && "urls" in source) {
|
|
68
|
+
return {
|
|
69
|
+
timeTakenMs: 0,
|
|
70
|
+
...source,
|
|
71
|
+
urls: source.urls
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (source.fetch)
|
|
75
|
+
return fetchDataSource(source);
|
|
76
|
+
return {
|
|
77
|
+
...source,
|
|
78
|
+
error: "Invalid source"
|
|
79
|
+
};
|
|
80
|
+
})
|
|
81
|
+
)).flat();
|
|
82
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { normalizeRuntimeFilters } from "../utils-pure.mjs";
|
|
2
|
+
import { useRuntimeConfig } from "#imports";
|
|
3
|
+
export * from "../utils-pure.mjs";
|
|
4
|
+
export function useSimpleSitemapRuntimeConfig(e) {
|
|
5
|
+
const clone = JSON.parse(JSON.stringify(useRuntimeConfig(e).sitemap));
|
|
6
|
+
for (const k in clone.sitemaps) {
|
|
7
|
+
const sitemap = clone.sitemaps[k];
|
|
8
|
+
sitemap.include = normalizeRuntimeFilters(sitemap.include);
|
|
9
|
+
sitemap.exclude = normalizeRuntimeFilters(sitemap.exclude);
|
|
10
|
+
clone.sitemaps[k] = sitemap;
|
|
11
|
+
}
|
|
12
|
+
return Object.freeze(clone);
|
|
13
|
+
}
|