@nuxtjs/sitemap 2.4.0 → 5.0.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/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.Y7HhHHAz.js +1 -0
- package/dist/client/_nuxt/IconCSS.6oz918NR.css +1 -0
- package/dist/client/_nuxt/IconCSS.Q-iLbQkL.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -0
- package/dist/client/_nuxt/builds/meta/4ef99937-9859-42d8-9d63-62e7809c4b65.json +1 -0
- package/dist/client/_nuxt/entry.EEjw95sW.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.Loh33F4g.js +1 -0
- package/dist/client/_nuxt/error-500.SLhS9LVu.css +1 -0
- package/dist/client/_nuxt/error-500.m13OPw8F.js +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 +229 -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 +80 -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
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
import { useNuxt, loadNuxtModuleInstance, createResolver, addTemplate, extendPages, defineNuxtModule, useLogger, hasNuxtModule, getNuxtModuleVersion, hasNuxtModuleCompatibility, addServerImports, addServerPlugin, addServerHandler, findPath, addPrerenderRoutes } from '@nuxt/kit';
|
|
2
|
+
import { parseURL, withLeadingSlash, withBase, joinURL, withoutLeadingSlash } from 'ufo';
|
|
3
|
+
import { assertSiteConfig, installNuxtSiteConfig } from 'nuxt-site-config-kit';
|
|
4
|
+
import { defu, createDefu } from 'defu';
|
|
5
|
+
import { readPackageJSON } from 'pkg-types';
|
|
6
|
+
import { statSync, existsSync } from 'node:fs';
|
|
7
|
+
import { extname, relative, dirname } from 'pathe';
|
|
8
|
+
import { provider, env } from 'std-env';
|
|
9
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { build } from 'nitropack';
|
|
13
|
+
import { withSiteUrl } from 'nuxt-site-config-kit/urls';
|
|
14
|
+
import 'site-config-stack/urls';
|
|
15
|
+
|
|
16
|
+
async function resolveUrls(urls) {
|
|
17
|
+
if (typeof urls === "function")
|
|
18
|
+
urls = urls();
|
|
19
|
+
urls = await urls;
|
|
20
|
+
return urls;
|
|
21
|
+
}
|
|
22
|
+
function deepForEachPage(pages, callback, fullpath = null, depth = 0) {
|
|
23
|
+
pages.forEach((page) => {
|
|
24
|
+
let currentPath;
|
|
25
|
+
if (page.path.startsWith("/"))
|
|
26
|
+
currentPath = page.path;
|
|
27
|
+
else
|
|
28
|
+
currentPath = page.path === "" ? fullpath : `${fullpath.replace(/\/$/, "")}/${page.path}`;
|
|
29
|
+
callback(page, currentPath || "", depth);
|
|
30
|
+
if (page.children)
|
|
31
|
+
deepForEachPage(page.children, callback, currentPath, depth + 1);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function convertNuxtPagesToSitemapEntries(pages, config) {
|
|
35
|
+
const routesNameSeparator = config.routesNameSeparator || "___";
|
|
36
|
+
let flattenedPages = [];
|
|
37
|
+
deepForEachPage(
|
|
38
|
+
pages,
|
|
39
|
+
(page, loc, depth) => {
|
|
40
|
+
flattenedPages.push({ page, loc, depth });
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
flattenedPages = flattenedPages.filter((page) => !page.loc.includes(":")).filter((page, idx, arr) => {
|
|
44
|
+
return !arr.find((p) => {
|
|
45
|
+
return p.loc === page.loc && p.depth > page.depth;
|
|
46
|
+
});
|
|
47
|
+
}).map((p) => {
|
|
48
|
+
delete p.depth;
|
|
49
|
+
return p;
|
|
50
|
+
});
|
|
51
|
+
const pagesWithMeta = flattenedPages.map((p) => {
|
|
52
|
+
if (config.autoLastmod && p.page.file) {
|
|
53
|
+
try {
|
|
54
|
+
const stats = statSync(p.page.file);
|
|
55
|
+
if (stats?.mtime)
|
|
56
|
+
p.lastmod = stats.mtime;
|
|
57
|
+
} catch (e) {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (p.page?.meta?.sitemap) {
|
|
61
|
+
p = defu(p.page.meta.sitemap, p);
|
|
62
|
+
}
|
|
63
|
+
return p;
|
|
64
|
+
});
|
|
65
|
+
const localeGroups = {};
|
|
66
|
+
pagesWithMeta.reduce((acc, e) => {
|
|
67
|
+
if (e.page.name?.includes(routesNameSeparator)) {
|
|
68
|
+
const [name, locale] = e.page.name.split(routesNameSeparator);
|
|
69
|
+
if (!acc[name])
|
|
70
|
+
acc[name] = [];
|
|
71
|
+
const { iso, code } = config.normalisedLocales.find((l) => l.code === locale) || { iso: locale, code: locale };
|
|
72
|
+
acc[name].push({ ...e, _sitemap: config.isI18nMapped ? iso || code : void 0, locale });
|
|
73
|
+
} else {
|
|
74
|
+
acc.default = acc.default || [];
|
|
75
|
+
acc.default.push(e);
|
|
76
|
+
}
|
|
77
|
+
return acc;
|
|
78
|
+
}, localeGroups);
|
|
79
|
+
return Object.entries(localeGroups).map(([locale, entries]) => {
|
|
80
|
+
if (locale === "default") {
|
|
81
|
+
return entries.map((e) => {
|
|
82
|
+
const [name] = (e.page?.name || "").split(routesNameSeparator);
|
|
83
|
+
if (localeGroups[name]?.some((a) => a.locale === config.defaultLocale))
|
|
84
|
+
return false;
|
|
85
|
+
const defaultLocale = config.normalisedLocales.find((l) => l.code === config.defaultLocale);
|
|
86
|
+
if (defaultLocale && config.isI18nMapped)
|
|
87
|
+
e._sitemap = defaultLocale.iso || defaultLocale.code;
|
|
88
|
+
delete e.page;
|
|
89
|
+
delete e.locale;
|
|
90
|
+
return { ...e };
|
|
91
|
+
}).filter(Boolean);
|
|
92
|
+
}
|
|
93
|
+
return entries.map((entry) => {
|
|
94
|
+
const alternatives = entries.map((entry2) => {
|
|
95
|
+
const hreflang = config.normalisedLocales.find((l) => l.code === entry2.locale)?.iso || entry2.locale;
|
|
96
|
+
return {
|
|
97
|
+
hreflang,
|
|
98
|
+
href: entry2.loc
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
const xDefault = entries.find((a) => a.locale === config.defaultLocale);
|
|
102
|
+
if (xDefault && alternatives.length) {
|
|
103
|
+
alternatives.push({
|
|
104
|
+
hreflang: "x-default",
|
|
105
|
+
href: xDefault.loc
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const e = { ...entry };
|
|
109
|
+
if (config.isI18nMapped) {
|
|
110
|
+
const { iso, code } = config.normalisedLocales.find((l) => l.code === entry.locale) || { iso: locale, code: locale };
|
|
111
|
+
e._sitemap = iso || code;
|
|
112
|
+
}
|
|
113
|
+
delete e.page;
|
|
114
|
+
delete e.locale;
|
|
115
|
+
return {
|
|
116
|
+
...e,
|
|
117
|
+
alternatives
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}).filter(Boolean).flat();
|
|
121
|
+
}
|
|
122
|
+
function generateExtraRoutesFromNuxtConfig(nuxt = useNuxt()) {
|
|
123
|
+
const routeRules = Object.entries(nuxt.options.routeRules || {}).filter(([k, v]) => {
|
|
124
|
+
if (k.includes("*") || k.includes(".") || k.includes(":"))
|
|
125
|
+
return false;
|
|
126
|
+
if (typeof v.index === "boolean" && !v.index)
|
|
127
|
+
return false;
|
|
128
|
+
return !v.redirect;
|
|
129
|
+
}).map(([k]) => k);
|
|
130
|
+
const prerenderUrls = (nuxt.options.nitro.prerender?.routes || []).filter((p) => p && !extname(p) && !p.startsWith("/api/"));
|
|
131
|
+
return { routeRules, prerenderUrls };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function getNuxtModuleOptions(module, nuxt = useNuxt()) {
|
|
135
|
+
const moduleMeta = (typeof module === "string" ? { name: module } : await module.getMeta?.()) || {};
|
|
136
|
+
const { nuxtModule } = await loadNuxtModuleInstance(module, nuxt);
|
|
137
|
+
let moduleEntry;
|
|
138
|
+
for (const m of nuxt.options.modules) {
|
|
139
|
+
if (Array.isArray(m) && m.length >= 2) {
|
|
140
|
+
const _module = m[0];
|
|
141
|
+
const _moduleEntryName = typeof _module === "string" ? _module : (await _module.getMeta?.())?.name || "";
|
|
142
|
+
if (_moduleEntryName === moduleMeta.name)
|
|
143
|
+
moduleEntry = m;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
let inlineOptions = {};
|
|
147
|
+
if (moduleEntry)
|
|
148
|
+
inlineOptions = moduleEntry[1];
|
|
149
|
+
if (nuxtModule.getOptions)
|
|
150
|
+
return nuxtModule.getOptions(inlineOptions, nuxt);
|
|
151
|
+
return inlineOptions;
|
|
152
|
+
}
|
|
153
|
+
function extendTypes(module, template) {
|
|
154
|
+
const nuxt = useNuxt();
|
|
155
|
+
const { resolve } = createResolver(import.meta.url);
|
|
156
|
+
addTemplate({
|
|
157
|
+
filename: `module/${module}.d.ts`,
|
|
158
|
+
getContents: async () => {
|
|
159
|
+
const typesPath = relative(resolve(nuxt.options.rootDir, nuxt.options.buildDir, "module"), resolve("runtime/types"));
|
|
160
|
+
const s = await template({ typesPath });
|
|
161
|
+
return `// Generated by ${module}
|
|
162
|
+
${s}
|
|
163
|
+
export {}
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
nuxt.hooks.hook("prepare:types", ({ references }) => {
|
|
168
|
+
references.push({ path: resolve(nuxt.options.buildDir, `module/${module}.d.ts`) });
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function createPagesPromise(nuxt = useNuxt()) {
|
|
172
|
+
return new Promise((resolve) => {
|
|
173
|
+
nuxt.hooks.hook("modules:done", () => {
|
|
174
|
+
extendPages(resolve);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function createNitroPromise(nuxt = useNuxt()) {
|
|
179
|
+
return new Promise((resolve) => {
|
|
180
|
+
nuxt.hooks.hook("nitro:init", (nitro) => {
|
|
181
|
+
resolve(nitro);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const autodetectableProviders = {
|
|
186
|
+
azure_static: "azure",
|
|
187
|
+
cloudflare_pages: "cloudflare-pages",
|
|
188
|
+
netlify: "netlify",
|
|
189
|
+
stormkit: "stormkit",
|
|
190
|
+
vercel: "vercel",
|
|
191
|
+
cleavr: "cleavr",
|
|
192
|
+
stackblitz: "stackblitz"
|
|
193
|
+
};
|
|
194
|
+
const autodetectableStaticProviders = {
|
|
195
|
+
netlify: "netlify-static",
|
|
196
|
+
vercel: "vercel-static"
|
|
197
|
+
};
|
|
198
|
+
function detectTarget(options = {}) {
|
|
199
|
+
return options?.static ? autodetectableStaticProviders[provider] : autodetectableProviders[provider];
|
|
200
|
+
}
|
|
201
|
+
function resolveNitroPreset(nitroConfig) {
|
|
202
|
+
if (provider === "stackblitz")
|
|
203
|
+
return "stackblitz";
|
|
204
|
+
let preset;
|
|
205
|
+
if (nitroConfig && nitroConfig?.preset)
|
|
206
|
+
preset = nitroConfig.preset;
|
|
207
|
+
if (!preset)
|
|
208
|
+
preset = env.NITRO_PRESET || detectTarget() || "node-server";
|
|
209
|
+
return preset.replace("_", "-");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function extractSitemapMetaFromHtml(html, options) {
|
|
213
|
+
options = options || { images: true, lastmod: true, alternatives: true };
|
|
214
|
+
const payload = {};
|
|
215
|
+
if (options?.images) {
|
|
216
|
+
const images = /* @__PURE__ */ new Set();
|
|
217
|
+
const mainRegex = /<main[^>]*>([\s\S]*?)<\/main>/;
|
|
218
|
+
const mainMatch = mainRegex.exec(html);
|
|
219
|
+
if (mainMatch?.[1] && mainMatch[1].includes("<img")) {
|
|
220
|
+
const imgRegex = /<img[^>]+src="([^">]+)"/g;
|
|
221
|
+
let match;
|
|
222
|
+
while ((match = imgRegex.exec(mainMatch[1])) !== null) {
|
|
223
|
+
if (match.index === imgRegex.lastIndex)
|
|
224
|
+
imgRegex.lastIndex++;
|
|
225
|
+
let url = match[1];
|
|
226
|
+
if (url.startsWith("/"))
|
|
227
|
+
url = withSiteUrl(url);
|
|
228
|
+
images.add(url);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (images.size > 0)
|
|
232
|
+
payload.images = [...images].map((i) => ({ loc: i }));
|
|
233
|
+
}
|
|
234
|
+
if (options?.lastmod) {
|
|
235
|
+
const articleModifiedTime = html.match(/<meta[^>]+property="article:modified_time"[^>]+content="([^"]+)"/)?.[1] || html.match(/<meta[^>]+content="([^"]+)"[^>]+property="article:modified_time"/)?.[1];
|
|
236
|
+
if (articleModifiedTime)
|
|
237
|
+
payload.lastmod = articleModifiedTime;
|
|
238
|
+
}
|
|
239
|
+
if (options?.alternatives) {
|
|
240
|
+
const alternatives = (html.match(/<link[^>]+rel="alternate"[^>]+>/g) || []).map((a) => {
|
|
241
|
+
const href = a.match(/href="([^"]+)"/)?.[1];
|
|
242
|
+
const hreflang = a.match(/hreflang="([^"]+)"/)?.[1];
|
|
243
|
+
return { hreflang, href: parseURL(href).pathname };
|
|
244
|
+
}).filter((a) => a.hreflang && a.href);
|
|
245
|
+
if (alternatives?.length && (alternatives.length > 1 || alternatives?.[0].hreflang !== "x-default"))
|
|
246
|
+
payload.alternatives = alternatives;
|
|
247
|
+
}
|
|
248
|
+
return payload;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const merger = createDefu((obj, key, value) => {
|
|
252
|
+
if (Array.isArray(obj[key]) && Array.isArray(value))
|
|
253
|
+
obj[key] = Array.from(/* @__PURE__ */ new Set([...obj[key], ...value]));
|
|
254
|
+
return obj[key];
|
|
255
|
+
});
|
|
256
|
+
function mergeOnKey(arr, key) {
|
|
257
|
+
const res = {};
|
|
258
|
+
arr.forEach((item) => {
|
|
259
|
+
const k = item[key];
|
|
260
|
+
res[k] = merger(item, res[k] || {});
|
|
261
|
+
});
|
|
262
|
+
return Object.values(res);
|
|
263
|
+
}
|
|
264
|
+
function splitForLocales(path, locales) {
|
|
265
|
+
const prefix = withLeadingSlash(path).split("/")[1];
|
|
266
|
+
if (locales.includes(prefix))
|
|
267
|
+
return [prefix, path.replace(`/${prefix}`, "")];
|
|
268
|
+
return [null, path];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function formatPrerenderRoute(route) {
|
|
272
|
+
let str = ` \u251C\u2500 ${route.route} (${route.generateTimeMS}ms)`;
|
|
273
|
+
if (route.error) {
|
|
274
|
+
const errorColor = chalk[route.error.statusCode === 404 ? "yellow" : "red"];
|
|
275
|
+
const errorLead = "\u2514\u2500\u2500";
|
|
276
|
+
str += `
|
|
277
|
+
\u2502 ${errorLead} ${errorColor(route.error)}`;
|
|
278
|
+
}
|
|
279
|
+
return chalk.gray(str);
|
|
280
|
+
}
|
|
281
|
+
function includesSitemapRoot(sitemapName, routes) {
|
|
282
|
+
return routes.includes(`/sitemap.xml`) || routes.includes(`/${sitemapName}`) || routes.includes("/sitemap_index.xml");
|
|
283
|
+
}
|
|
284
|
+
function isNuxtGenerate(nuxt = useNuxt()) {
|
|
285
|
+
return nuxt.options._generate || nuxt.options.nitro.static || nuxt.options.nitro.preset === "static";
|
|
286
|
+
}
|
|
287
|
+
function setupPrerenderHandler(options, nuxt = useNuxt()) {
|
|
288
|
+
const prerenderedRoutes = nuxt.options.nitro.prerender?.routes || [];
|
|
289
|
+
const prerenderSitemap = isNuxtGenerate() || includesSitemapRoot(options.sitemapName, prerenderedRoutes);
|
|
290
|
+
if (nuxt.options.nitro.prerender?.routes)
|
|
291
|
+
nuxt.options.nitro.prerender.routes = nuxt.options.nitro.prerender.routes.filter((r) => r && !includesSitemapRoot(options.sitemapName, [r]));
|
|
292
|
+
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
293
|
+
let prerenderer;
|
|
294
|
+
nitro.hooks.hook("prerender:init", async (_prerenderer) => {
|
|
295
|
+
prerenderer = _prerenderer;
|
|
296
|
+
assertSiteConfig("@nuxtjs/sitemap", {
|
|
297
|
+
url: "Required to generate absolute canonical URLs for your sitemap."
|
|
298
|
+
}, { throwError: false });
|
|
299
|
+
});
|
|
300
|
+
nitro.hooks.hook("prerender:generate", async (route) => {
|
|
301
|
+
const html = route.contents;
|
|
302
|
+
if (!route.fileName?.endsWith(".html") || !html)
|
|
303
|
+
return;
|
|
304
|
+
route._sitemap = defu(route._sitemap, {
|
|
305
|
+
loc: route.route
|
|
306
|
+
});
|
|
307
|
+
if (options.autoI18n && Object.keys(options.sitemaps).length > 1) {
|
|
308
|
+
const path = route.route;
|
|
309
|
+
const match = splitForLocales(path, options.autoI18n.locales.map((l) => l.code));
|
|
310
|
+
const locale = match[0] || options.autoI18n.defaultLocale;
|
|
311
|
+
if (options.isI18nMapped) {
|
|
312
|
+
const { code, iso } = options.autoI18n.locales.find((l) => l.code === locale) || { code: locale, iso: locale };
|
|
313
|
+
route._sitemap._sitemap = iso || code;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
route._sitemap = defu(extractSitemapMetaFromHtml(html, {
|
|
317
|
+
images: options.discoverImages,
|
|
318
|
+
// TODO configurable?
|
|
319
|
+
lastmod: true,
|
|
320
|
+
alternatives: true
|
|
321
|
+
}), route._sitemap);
|
|
322
|
+
});
|
|
323
|
+
nitro.hooks.hook("prerender:done", async () => {
|
|
324
|
+
await build(prerenderer);
|
|
325
|
+
const routes = [];
|
|
326
|
+
if (options.debug)
|
|
327
|
+
routes.push("/__sitemap__/debug.json");
|
|
328
|
+
if (prerenderSitemap) {
|
|
329
|
+
routes.push(
|
|
330
|
+
options.isMultiSitemap ? "/sitemap_index.xml" : `/${Object.keys(options.sitemaps)[0]}`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
for (const route of routes)
|
|
334
|
+
await prerenderRoute(nitro, route);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
async function prerenderRoute(nitro, route) {
|
|
339
|
+
const start = Date.now();
|
|
340
|
+
const _route = { route, fileName: route };
|
|
341
|
+
const encodedRoute = encodeURI(route);
|
|
342
|
+
const res = await globalThis.$fetch.raw(
|
|
343
|
+
withBase(encodedRoute, nitro.options.baseURL),
|
|
344
|
+
{
|
|
345
|
+
headers: { "x-nitro-prerender": encodedRoute },
|
|
346
|
+
retry: nitro.options.prerender.retry,
|
|
347
|
+
retryDelay: nitro.options.prerender.retryDelay
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
const header = res.headers.get("x-nitro-prerender") || "";
|
|
351
|
+
const prerenderUrls = [
|
|
352
|
+
...header.split(",").map((i) => i.trim()).map((i) => decodeURIComponent(i)).filter(Boolean)
|
|
353
|
+
];
|
|
354
|
+
const filePath = join(nitro.options.output.publicDir, _route.fileName);
|
|
355
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
356
|
+
const data = res._data;
|
|
357
|
+
if (filePath.endsWith("json") || typeof data === "object")
|
|
358
|
+
await writeFile(filePath, JSON.stringify(data), "utf8");
|
|
359
|
+
else
|
|
360
|
+
await writeFile(filePath, data, "utf8");
|
|
361
|
+
_route.generateTimeMS = Date.now() - start;
|
|
362
|
+
nitro._prerenderedRoutes.push(_route);
|
|
363
|
+
nitro.logger.log(formatPrerenderRoute(_route));
|
|
364
|
+
for (const url of prerenderUrls)
|
|
365
|
+
await prerenderRoute(nitro, url);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const DEVTOOLS_UI_ROUTE = "/__sitemap__/devtools";
|
|
369
|
+
const DEVTOOLS_UI_LOCAL_PORT = 3030;
|
|
370
|
+
function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
|
|
371
|
+
const clientPath = resolve("./client");
|
|
372
|
+
const isProductionBuild = existsSync(clientPath);
|
|
373
|
+
if (isProductionBuild) {
|
|
374
|
+
nuxt.hook("vite:serverCreated", async (server) => {
|
|
375
|
+
const sirv = await import('sirv').then((r) => r.default || r);
|
|
376
|
+
server.middlewares.use(
|
|
377
|
+
DEVTOOLS_UI_ROUTE,
|
|
378
|
+
sirv(clientPath, { dev: true, single: true })
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
} else {
|
|
382
|
+
nuxt.hook("vite:extendConfig", (config) => {
|
|
383
|
+
config.server = config.server || {};
|
|
384
|
+
config.server.proxy = config.server.proxy || {};
|
|
385
|
+
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
|
|
386
|
+
target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
|
|
387
|
+
changeOrigin: true,
|
|
388
|
+
followRedirects: true,
|
|
389
|
+
rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
|
|
390
|
+
};
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
394
|
+
tabs.push({
|
|
395
|
+
// unique identifier
|
|
396
|
+
name: "sitemap",
|
|
397
|
+
// title to display in the tab
|
|
398
|
+
title: "Sitemap",
|
|
399
|
+
// any icon from Iconify, or a URL to an image
|
|
400
|
+
icon: "carbon:load-balancer-application",
|
|
401
|
+
// iframe view
|
|
402
|
+
view: {
|
|
403
|
+
type: "iframe",
|
|
404
|
+
src: DEVTOOLS_UI_ROUTE
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function normaliseDate(d) {
|
|
411
|
+
if (typeof d === "string") {
|
|
412
|
+
d = d.replace("Z", "");
|
|
413
|
+
d = d.replace(/\.\d+$/, "");
|
|
414
|
+
if (d.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/) || d.match(/^\d{4}-\d{2}-\d{2}$/))
|
|
415
|
+
return d;
|
|
416
|
+
d = new Date(d);
|
|
417
|
+
if (Number.isNaN(d.getTime()))
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
const z = (n) => `0${n}`.slice(-2);
|
|
421
|
+
return `${d.getUTCFullYear()}-${z(d.getUTCMonth() + 1)}-${z(d.getUTCDate())}T${z(d.getUTCHours())}:${z(d.getUTCMinutes())}:${z(d.getUTCSeconds())}+00:00`;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function splitPathForI18nLocales(path, autoI18n) {
|
|
425
|
+
const locales = autoI18n.strategy === "prefix_except_default" ? autoI18n.locales.filter((l) => l.code !== autoI18n.defaultLocale) : autoI18n.locales;
|
|
426
|
+
if (typeof path !== "string" || path.startsWith("/api") || path.startsWith("/_nuxt"))
|
|
427
|
+
return path;
|
|
428
|
+
const match = splitForLocales(path, locales.map((l) => l.code));
|
|
429
|
+
const locale = match[0];
|
|
430
|
+
if (locale)
|
|
431
|
+
return path;
|
|
432
|
+
return [
|
|
433
|
+
path,
|
|
434
|
+
...locales.map((l) => `/${l.code}${path}`)
|
|
435
|
+
];
|
|
436
|
+
}
|
|
437
|
+
function generatePathForI18nPages({ localeCode, pageLocales, nuxtI18nConfig, forcedStrategy }) {
|
|
438
|
+
switch (forcedStrategy ?? nuxtI18nConfig.strategy) {
|
|
439
|
+
case "prefix_except_default":
|
|
440
|
+
case "prefix_and_default":
|
|
441
|
+
return localeCode === nuxtI18nConfig.defaultLocale ? pageLocales : joinURL(localeCode, pageLocales);
|
|
442
|
+
case "prefix":
|
|
443
|
+
return joinURL(localeCode, pageLocales);
|
|
444
|
+
case "no_prefix":
|
|
445
|
+
default:
|
|
446
|
+
return pageLocales;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function isValidFilter(filter) {
|
|
451
|
+
if (typeof filter === "string")
|
|
452
|
+
return true;
|
|
453
|
+
if (filter instanceof RegExp)
|
|
454
|
+
return true;
|
|
455
|
+
if (typeof filter === "object" && typeof filter.regex === "string")
|
|
456
|
+
return true;
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
function normalizeFilters(filters) {
|
|
460
|
+
return (filters || []).map((filter) => {
|
|
461
|
+
if (!isValidFilter(filter)) {
|
|
462
|
+
console.warn(`[@nuxtjs/sitemap] You have provided an invalid filter: ${filter}, ignoring.`);
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
return filter instanceof RegExp ? { regex: filter.toString() } : filter;
|
|
466
|
+
}).filter(Boolean);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const module = defineNuxtModule({
|
|
470
|
+
meta: {
|
|
471
|
+
name: "@nuxtjs/sitemap",
|
|
472
|
+
compatibility: {
|
|
473
|
+
nuxt: "^3.9.0",
|
|
474
|
+
bridge: false
|
|
475
|
+
},
|
|
476
|
+
configKey: "sitemap"
|
|
477
|
+
},
|
|
478
|
+
defaults: {
|
|
479
|
+
enabled: true,
|
|
480
|
+
credits: true,
|
|
481
|
+
cacheMaxAgeSeconds: 60 * 10,
|
|
482
|
+
// cache for 10 minutes
|
|
483
|
+
debug: false,
|
|
484
|
+
defaultSitemapsChunkSize: 1e3,
|
|
485
|
+
autoLastmod: false,
|
|
486
|
+
discoverImages: true,
|
|
487
|
+
dynamicUrlsApiEndpoint: "/api/_sitemap-urls",
|
|
488
|
+
urls: [],
|
|
489
|
+
sortEntries: true,
|
|
490
|
+
xsl: "/__sitemap__/style.xsl",
|
|
491
|
+
xslTips: true,
|
|
492
|
+
strictNuxtContentPaths: false,
|
|
493
|
+
runtimeCacheStorage: true,
|
|
494
|
+
sitemapName: "sitemap.xml",
|
|
495
|
+
// cacheControlHeader: 'max-age=600, must-revalidate',
|
|
496
|
+
defaults: {},
|
|
497
|
+
// index sitemap options filtering
|
|
498
|
+
include: [],
|
|
499
|
+
exclude: ["/_nuxt/**", "/api/**"],
|
|
500
|
+
// sources
|
|
501
|
+
sources: [],
|
|
502
|
+
excludeAppSources: [],
|
|
503
|
+
inferStaticPagesAsRoutes: true
|
|
504
|
+
},
|
|
505
|
+
async setup(config, nuxt) {
|
|
506
|
+
const { resolve } = createResolver(import.meta.url);
|
|
507
|
+
const { name, version } = await readPackageJSON(resolve("../package.json"));
|
|
508
|
+
const logger = useLogger(name);
|
|
509
|
+
logger.level = config.debug || nuxt.options.debug ? 4 : 3;
|
|
510
|
+
if (config.enabled === false) {
|
|
511
|
+
logger.debug("The module is disabled, skipping setup.");
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
config.xslColumns = config.xslColumns || [
|
|
515
|
+
{ label: "URL", width: "50%" },
|
|
516
|
+
{ label: "Images", width: "25%", select: "count(image:image)" },
|
|
517
|
+
{
|
|
518
|
+
label: "Last Updated",
|
|
519
|
+
width: "25%",
|
|
520
|
+
select: "concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)),concat(' ', substring(sitemap:lastmod,20,6)))"
|
|
521
|
+
}
|
|
522
|
+
];
|
|
523
|
+
if (config.autoLastmod) {
|
|
524
|
+
config.defaults = config.defaults || {};
|
|
525
|
+
config.defaults.lastmod = normaliseDate(/* @__PURE__ */ new Date());
|
|
526
|
+
}
|
|
527
|
+
await installNuxtSiteConfig();
|
|
528
|
+
const userGlobalSources = [
|
|
529
|
+
...config.sources || []
|
|
530
|
+
];
|
|
531
|
+
const appGlobalSources = [];
|
|
532
|
+
nuxt.options.nitro.storage = nuxt.options.nitro.storage || {};
|
|
533
|
+
if (config.runtimeCacheStorage && !nuxt.options.dev && typeof config.runtimeCacheStorage === "object")
|
|
534
|
+
nuxt.options.nitro.storage.sitemap = config.runtimeCacheStorage;
|
|
535
|
+
if (!config.sitemapName.endsWith("xml")) {
|
|
536
|
+
const newName = `${config.sitemapName.split(".")[0]}.xml`;
|
|
537
|
+
logger.warn(`You have provided a \`sitemapName\` that does not end with \`.xml\`. This is not supported by search engines, renaming to \`${newName}\`.`);
|
|
538
|
+
config.sitemapName = newName;
|
|
539
|
+
}
|
|
540
|
+
config.sitemapName = withoutLeadingSlash(config.sitemapName);
|
|
541
|
+
let usingMultiSitemaps = !!config.sitemaps;
|
|
542
|
+
let isI18nMapped = false;
|
|
543
|
+
let nuxtI18nConfig = {};
|
|
544
|
+
let resolvedAutoI18n = typeof config.autoI18n === "boolean" ? false : config.autoI18n || false;
|
|
545
|
+
const hasDisabledAutoI18n = typeof config.autoI18n === "boolean" && !config.autoI18n;
|
|
546
|
+
let normalisedLocales = [];
|
|
547
|
+
if (hasNuxtModule("@nuxtjs/i18n")) {
|
|
548
|
+
const i18nVersion = await getNuxtModuleVersion("@nuxtjs/i18n");
|
|
549
|
+
if (!await hasNuxtModuleCompatibility("@nuxtjs/i18n", ">=8"))
|
|
550
|
+
logger.warn(`You are using @nuxtjs/i18n v${i18nVersion}. For the best compatibility, please upgrade to @nuxtjs/i18n v8.0.0 or higher.`);
|
|
551
|
+
nuxtI18nConfig = await getNuxtModuleOptions("@nuxtjs/i18n") || {};
|
|
552
|
+
normalisedLocales = mergeOnKey((nuxtI18nConfig.locales || []).map((locale) => typeof locale === "string" ? { code: locale } : locale), "code");
|
|
553
|
+
const usingI18nPages = Object.keys(nuxtI18nConfig.pages || {}).length;
|
|
554
|
+
if (usingI18nPages && !hasDisabledAutoI18n) {
|
|
555
|
+
const i18nPagesSources = {
|
|
556
|
+
context: {
|
|
557
|
+
name: "@nuxtjs/i18n:pages",
|
|
558
|
+
description: "Generated from your i18n.pages config.",
|
|
559
|
+
tips: [
|
|
560
|
+
"You can disable this with `autoI18n: false`."
|
|
561
|
+
]
|
|
562
|
+
},
|
|
563
|
+
urls: []
|
|
564
|
+
};
|
|
565
|
+
for (const pageLocales of Object.values(nuxtI18nConfig?.pages)) {
|
|
566
|
+
for (const localeCode in pageLocales) {
|
|
567
|
+
const locale = normalisedLocales.find((l) => l.code === localeCode);
|
|
568
|
+
if (!locale || !pageLocales[localeCode] || pageLocales[localeCode].includes("["))
|
|
569
|
+
continue;
|
|
570
|
+
const alternatives = Object.keys(pageLocales).map((l) => ({
|
|
571
|
+
hreflang: normalisedLocales.find((nl) => nl.code === l)?.iso || l,
|
|
572
|
+
href: generatePathForI18nPages({ localeCode: l, pageLocales: pageLocales[l], nuxtI18nConfig })
|
|
573
|
+
}));
|
|
574
|
+
if (alternatives.length && nuxtI18nConfig.defaultLocale && pageLocales[nuxtI18nConfig.defaultLocale])
|
|
575
|
+
alternatives.push({ hreflang: "x-default", href: generatePathForI18nPages({ localeCode: nuxtI18nConfig.defaultLocale, pageLocales: pageLocales[nuxtI18nConfig.defaultLocale], nuxtI18nConfig }) });
|
|
576
|
+
i18nPagesSources.urls.push({
|
|
577
|
+
_sitemap: locale.iso || locale.code,
|
|
578
|
+
loc: generatePathForI18nPages({ localeCode, pageLocales: pageLocales[localeCode], nuxtI18nConfig }),
|
|
579
|
+
alternatives
|
|
580
|
+
});
|
|
581
|
+
if (nuxtI18nConfig.strategy === "prefix_and_default" && localeCode === nuxtI18nConfig.defaultLocale) {
|
|
582
|
+
i18nPagesSources.urls.push({
|
|
583
|
+
_sitemap: locale.iso || locale.code,
|
|
584
|
+
loc: generatePathForI18nPages({ localeCode, pageLocales: pageLocales[localeCode], nuxtI18nConfig, forcedStrategy: "prefix" }),
|
|
585
|
+
alternatives
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
appGlobalSources.push(i18nPagesSources);
|
|
591
|
+
if (Array.isArray(config.excludeAppSources))
|
|
592
|
+
config.excludeAppSources.push("nuxt:pages");
|
|
593
|
+
} else {
|
|
594
|
+
if (!normalisedLocales.length)
|
|
595
|
+
logger.warn(`You are using @nuxtjs/i18n but have not configured any locales, this will cause issues with ${name}. Please configure \`locales\`.`);
|
|
596
|
+
}
|
|
597
|
+
const hasSetAutoI18n = typeof config.autoI18n === "object" && Object.keys(config.autoI18n).length;
|
|
598
|
+
const hasI18nConfigForAlternatives = nuxtI18nConfig.differentDomains || usingI18nPages || nuxtI18nConfig.strategy !== "no_prefix" && nuxtI18nConfig.locales;
|
|
599
|
+
if (!hasSetAutoI18n && !hasDisabledAutoI18n && hasI18nConfigForAlternatives) {
|
|
600
|
+
resolvedAutoI18n = {
|
|
601
|
+
differentDomains: nuxtI18nConfig.differentDomains,
|
|
602
|
+
defaultLocale: nuxtI18nConfig.defaultLocale,
|
|
603
|
+
locales: normalisedLocales,
|
|
604
|
+
strategy: nuxtI18nConfig.strategy
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
if (typeof config.sitemaps === "undefined" && !!resolvedAutoI18n && nuxtI18nConfig.strategy !== "no_prefix") {
|
|
608
|
+
config.sitemaps = { index: [] };
|
|
609
|
+
for (const locale of resolvedAutoI18n.locales) {
|
|
610
|
+
config.sitemaps[locale.iso || locale.code] = { includeAppSources: true };
|
|
611
|
+
}
|
|
612
|
+
isI18nMapped = true;
|
|
613
|
+
usingMultiSitemaps = true;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
let needsRobotsPolyfill = true;
|
|
617
|
+
if (hasNuxtModule("nuxt-simple-robots")) {
|
|
618
|
+
const robotsVersion = await getNuxtModuleVersion("nuxt-simple-robots");
|
|
619
|
+
if (!await hasNuxtModuleCompatibility("nuxt-simple-robots", ">=4"))
|
|
620
|
+
logger.warn(`You are using nuxt-simple-robots v${robotsVersion}. For the best compatibility, please upgrade to nuxt-simple-robots v4.0.0 or higher.`);
|
|
621
|
+
else
|
|
622
|
+
needsRobotsPolyfill = false;
|
|
623
|
+
nuxt.hooks.hook("robots:config", (robotsConfig) => {
|
|
624
|
+
robotsConfig.sitemap.push(usingMultiSitemaps ? "/sitemap_index.xml" : `/${config.sitemapName}`);
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
if (needsRobotsPolyfill) {
|
|
628
|
+
addServerImports([{
|
|
629
|
+
name: "getPathRobotConfigPolyfill",
|
|
630
|
+
as: "getPathRobotConfig",
|
|
631
|
+
from: resolve("./runtime/nitro/composables/getPathRobotConfigPolyfill")
|
|
632
|
+
}]);
|
|
633
|
+
}
|
|
634
|
+
extendTypes(name, async ({ typesPath }) => {
|
|
635
|
+
return `
|
|
636
|
+
declare module 'nitropack' {
|
|
637
|
+
interface NitroRouteRules {
|
|
638
|
+
index?: boolean
|
|
639
|
+
sitemap?: import('${typesPath}').SitemapItemDefaults
|
|
640
|
+
}
|
|
641
|
+
interface NitroRouteConfig {
|
|
642
|
+
index?: boolean
|
|
643
|
+
sitemap?: import('${typesPath}').SitemapItemDefaults
|
|
644
|
+
}
|
|
645
|
+
interface NitroRuntimeHooks {
|
|
646
|
+
'sitemap:resolved': (ctx: import('${typesPath}').SitemapRenderCtx) => void | Promise<void>
|
|
647
|
+
'sitemap:output': (ctx: import('${typesPath}').SitemapOutputHookCtx) => void | Promise<void>
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
declare module 'vue-router' {
|
|
651
|
+
interface RouteMeta {
|
|
652
|
+
sitemap?: import('${typesPath}').SitemapItemDefaults
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
`;
|
|
656
|
+
});
|
|
657
|
+
const nitroPreset = resolveNitroPreset();
|
|
658
|
+
const prerenderedRoutes = nuxt.options.nitro.prerender?.routes || [];
|
|
659
|
+
const prerenderSitemap = isNuxtGenerate() || includesSitemapRoot(config.sitemapName, prerenderedRoutes);
|
|
660
|
+
const routeRules = {};
|
|
661
|
+
nuxt.options.nitro.routeRules = nuxt.options.nitro.routeRules || {};
|
|
662
|
+
if (prerenderSitemap) {
|
|
663
|
+
routeRules.headers = {
|
|
664
|
+
"Content-Type": "text/xml; charset=UTF-8",
|
|
665
|
+
"Cache-Control": config.cacheMaxAgeSeconds ? `public, max-age=${config.cacheMaxAgeSeconds}, must-revalidate` : "no-cache, no-store",
|
|
666
|
+
"X-Sitemap-Prerendered": (/* @__PURE__ */ new Date()).toISOString()
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
if (!nuxt.options.dev && !isNuxtGenerate() && config.cacheMaxAgeSeconds && config.runtimeCacheStorage !== false) {
|
|
670
|
+
routeRules[nitroPreset.includes("vercel") ? "isr" : "swr"] = config.cacheMaxAgeSeconds;
|
|
671
|
+
routeRules.cache = {
|
|
672
|
+
// handle multi-tenancy
|
|
673
|
+
swr: true,
|
|
674
|
+
maxAge: config.cacheMaxAgeSeconds,
|
|
675
|
+
varies: ["X-Forwarded-Host", "X-Forwarded-Proto", "Host"]
|
|
676
|
+
};
|
|
677
|
+
if (typeof config.runtimeCacheStorage === "object")
|
|
678
|
+
routeRules.cache.base = "sitemap";
|
|
679
|
+
}
|
|
680
|
+
nuxt.options.nitro.routeRules["/sitemap.xsl"] = {
|
|
681
|
+
headers: {
|
|
682
|
+
"Content-Type": "application/xslt+xml"
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
if (usingMultiSitemaps) {
|
|
686
|
+
nuxt.options.nitro.routeRules["/sitemap.xml"] = { redirect: "/sitemap_index.xml" };
|
|
687
|
+
nuxt.options.nitro.routeRules["/sitemap_index.xml"] = routeRules;
|
|
688
|
+
if (typeof config.sitemaps === "object") {
|
|
689
|
+
for (const k in config.sitemaps)
|
|
690
|
+
nuxt.options.nitro.routeRules[`/${k}-sitemap.xml`] = routeRules;
|
|
691
|
+
} else {
|
|
692
|
+
nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
|
|
693
|
+
}
|
|
694
|
+
} else {
|
|
695
|
+
nuxt.options.nitro.routeRules[`/${config.sitemapName}`] = routeRules;
|
|
696
|
+
}
|
|
697
|
+
if (config.experimentalWarmUp)
|
|
698
|
+
addServerPlugin(resolve("./runtime/nitro/plugins/warm-up"));
|
|
699
|
+
if (config.experimentalCompression)
|
|
700
|
+
addServerPlugin(resolve("./runtime/nitro/plugins/compression"));
|
|
701
|
+
const isNuxtContentDocumentDriven = !!nuxt.options.content?.documentDriven || config.strictNuxtContentPaths;
|
|
702
|
+
if (hasNuxtModule("@nuxt/content")) {
|
|
703
|
+
addServerPlugin(resolve("./runtime/nitro/plugins/nuxt-content"));
|
|
704
|
+
addServerHandler({
|
|
705
|
+
route: "/__sitemap__/nuxt-content-urls.json",
|
|
706
|
+
handler: resolve("./runtime/nitro/routes/__sitemap__/nuxt-content-urls")
|
|
707
|
+
});
|
|
708
|
+
const tips = [];
|
|
709
|
+
if (nuxt.options.content?.documentDriven)
|
|
710
|
+
tips.push("Enabled because you're using `@nuxt/content` with `documentDriven: true`.");
|
|
711
|
+
else if (config.strictNuxtContentPaths)
|
|
712
|
+
tips.push("Enabled because you've set `config.strictNuxtContentPaths: true`.");
|
|
713
|
+
else
|
|
714
|
+
tips.push("You can provide a `sitemap` key in your markdown frontmatter to configure specific URLs. Make sure you include a `loc`.");
|
|
715
|
+
appGlobalSources.push({
|
|
716
|
+
context: {
|
|
717
|
+
name: "@nuxt/content:urls",
|
|
718
|
+
description: "Generated from your markdown files.",
|
|
719
|
+
tips
|
|
720
|
+
},
|
|
721
|
+
fetch: "/__sitemap__/nuxt-content-urls.json"
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
const hasLegacyDefaultApiSource = !!await findPath(resolve(nuxt.options.serverDir, "api/_sitemap-urls"));
|
|
725
|
+
if (
|
|
726
|
+
// make sure they didn't manually add it as a source
|
|
727
|
+
!config.sources?.includes("/api/_sitemap-urls") && (hasLegacyDefaultApiSource || config.dynamicUrlsApiEndpoint !== "/api/_sitemap-urls")
|
|
728
|
+
) {
|
|
729
|
+
userGlobalSources.push({
|
|
730
|
+
context: {
|
|
731
|
+
name: "dynamicUrlsApiEndpoint",
|
|
732
|
+
description: "Generated from your dynamicUrlsApiEndpoint config.",
|
|
733
|
+
tips: [
|
|
734
|
+
"The `dynamicUrlsApiEndpoint` config is deprecated.",
|
|
735
|
+
hasLegacyDefaultApiSource ? "Consider renaming the `api/_sitemap-urls` file and add it the `sitemap.sources` config instead. This provides more explicit sitemap generation." : "Consider switching to using the `sitemap.sources` config which also supports fetch options."
|
|
736
|
+
]
|
|
737
|
+
},
|
|
738
|
+
fetch: hasLegacyDefaultApiSource ? "/api/_sitemap-urls" : config.dynamicUrlsApiEndpoint
|
|
739
|
+
});
|
|
740
|
+
} else {
|
|
741
|
+
config.dynamicUrlsApiEndpoint = false;
|
|
742
|
+
}
|
|
743
|
+
const sitemaps = {};
|
|
744
|
+
if (usingMultiSitemaps) {
|
|
745
|
+
addServerHandler({
|
|
746
|
+
route: "/sitemap_index.xml",
|
|
747
|
+
handler: resolve("./runtime/nitro/routes/sitemap_index.xml")
|
|
748
|
+
});
|
|
749
|
+
sitemaps.index = {
|
|
750
|
+
sitemapName: "index",
|
|
751
|
+
_route: withBase("sitemap_index.xml", nuxt.options.app.baseURL || "/"),
|
|
752
|
+
// TODO better index support
|
|
753
|
+
// @ts-expect-error untyped
|
|
754
|
+
sitemaps: config.sitemaps.index || []
|
|
755
|
+
};
|
|
756
|
+
if (typeof config.sitemaps === "object") {
|
|
757
|
+
for (const sitemapName in config.sitemaps) {
|
|
758
|
+
if (sitemapName === "index")
|
|
759
|
+
continue;
|
|
760
|
+
addServerHandler({
|
|
761
|
+
route: `/${sitemapName}-sitemap.xml`,
|
|
762
|
+
handler: resolve("./runtime/nitro/middleware/[sitemap]-sitemap.xml")
|
|
763
|
+
});
|
|
764
|
+
const definition = config.sitemaps[sitemapName];
|
|
765
|
+
sitemaps[sitemapName] = defu(
|
|
766
|
+
{
|
|
767
|
+
sitemapName,
|
|
768
|
+
_route: withBase(`${sitemapName}-sitemap.xml`, nuxt.options.app.baseURL || "/"),
|
|
769
|
+
_hasSourceChunk: typeof definition.urls !== "undefined" || definition.sources?.length || !!definition.dynamicUrlsApiEndpoint
|
|
770
|
+
},
|
|
771
|
+
{ ...definition, urls: void 0, sources: void 0 },
|
|
772
|
+
{ include: config.include, exclude: config.exclude }
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
} else {
|
|
776
|
+
addServerHandler({
|
|
777
|
+
handler: resolve("./runtime/nitro/middleware/[sitemap]-sitemap.xml")
|
|
778
|
+
});
|
|
779
|
+
sitemaps.chunks = {
|
|
780
|
+
sitemapName: "chunks",
|
|
781
|
+
defaults: config.defaults,
|
|
782
|
+
include: config.include,
|
|
783
|
+
exclude: config.exclude,
|
|
784
|
+
includeAppSources: true
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
} else {
|
|
788
|
+
sitemaps[config.sitemapName] = {
|
|
789
|
+
sitemapName: config.sitemapName,
|
|
790
|
+
route: withBase(config.sitemapName, nuxt.options.app.baseURL || "/"),
|
|
791
|
+
// will contain the xml
|
|
792
|
+
defaults: config.defaults,
|
|
793
|
+
include: config.include,
|
|
794
|
+
exclude: config.exclude,
|
|
795
|
+
includeAppSources: true
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
if (resolvedAutoI18n && resolvedAutoI18n.locales && resolvedAutoI18n.strategy !== "no_prefix") {
|
|
799
|
+
const i18n = resolvedAutoI18n;
|
|
800
|
+
for (const sitemapName in sitemaps) {
|
|
801
|
+
if (["index", "chunks"].includes(sitemapName))
|
|
802
|
+
continue;
|
|
803
|
+
const sitemap = sitemaps[sitemapName];
|
|
804
|
+
sitemap.include = (sitemap.include || []).map((path) => splitPathForI18nLocales(path, i18n)).flat();
|
|
805
|
+
sitemap.exclude = (sitemap.exclude || []).map((path) => splitPathForI18nLocales(path, i18n)).flat();
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
for (const sitemapName in sitemaps) {
|
|
809
|
+
const sitemap = sitemaps[sitemapName];
|
|
810
|
+
sitemap.include = normalizeFilters(sitemap.include);
|
|
811
|
+
sitemap.exclude = normalizeFilters(sitemap.exclude);
|
|
812
|
+
}
|
|
813
|
+
const runtimeConfig = {
|
|
814
|
+
isI18nMapped,
|
|
815
|
+
sitemapName: config.sitemapName,
|
|
816
|
+
isMultiSitemap: usingMultiSitemaps,
|
|
817
|
+
excludeAppSources: config.excludeAppSources,
|
|
818
|
+
cacheMaxAgeSeconds: nuxt.options.dev ? 0 : config.cacheMaxAgeSeconds,
|
|
819
|
+
autoLastmod: config.autoLastmod,
|
|
820
|
+
defaultSitemapsChunkSize: config.defaultSitemapsChunkSize,
|
|
821
|
+
sortEntries: config.sortEntries,
|
|
822
|
+
debug: config.debug,
|
|
823
|
+
// needed for nuxt/content integration and prerendering
|
|
824
|
+
discoverImages: config.discoverImages,
|
|
825
|
+
/* @nuxt/content */
|
|
826
|
+
isNuxtContentDocumentDriven,
|
|
827
|
+
/* xsl styling */
|
|
828
|
+
xsl: config.xsl,
|
|
829
|
+
xslTips: config.xslTips,
|
|
830
|
+
xslColumns: config.xslColumns,
|
|
831
|
+
credits: config.credits,
|
|
832
|
+
version,
|
|
833
|
+
sitemaps
|
|
834
|
+
};
|
|
835
|
+
if (resolvedAutoI18n)
|
|
836
|
+
runtimeConfig.autoI18n = resolvedAutoI18n;
|
|
837
|
+
nuxt.options.runtimeConfig.sitemap = runtimeConfig;
|
|
838
|
+
if (config.debug || nuxt.options.dev) {
|
|
839
|
+
addServerHandler({
|
|
840
|
+
route: "/__sitemap__/debug.json",
|
|
841
|
+
handler: resolve("./runtime/nitro/routes/__sitemap__/debug")
|
|
842
|
+
});
|
|
843
|
+
setupDevToolsUI(config, resolve);
|
|
844
|
+
}
|
|
845
|
+
if (!config.inferStaticPagesAsRoutes)
|
|
846
|
+
config.excludeAppSources = true;
|
|
847
|
+
const imports = [
|
|
848
|
+
{
|
|
849
|
+
from: resolve("./runtime/nitro/composables/defineSitemapEventHandler"),
|
|
850
|
+
name: "defineSitemapEventHandler"
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
from: resolve("./runtime/nitro/composables/asSitemapUrl"),
|
|
854
|
+
name: "asSitemapUrl"
|
|
855
|
+
}
|
|
856
|
+
];
|
|
857
|
+
addServerImports(imports);
|
|
858
|
+
const pagesPromise = createPagesPromise();
|
|
859
|
+
const nitroPromise = createNitroPromise();
|
|
860
|
+
let resolvedConfigUrls = false;
|
|
861
|
+
nuxt.hooks.hook("nitro:config", (nitroConfig) => {
|
|
862
|
+
nitroConfig.virtual["#sitemap/global-sources.mjs"] = async () => {
|
|
863
|
+
const { prerenderUrls, routeRules: routeRules2 } = generateExtraRoutesFromNuxtConfig();
|
|
864
|
+
const prerenderUrlsFinal = [
|
|
865
|
+
...prerenderUrls,
|
|
866
|
+
...((await nitroPromise)._prerenderedRoutes || []).filter((r) => (!r.fileName || r.fileName.endsWith(".html")) && !r.route.endsWith(".html") && !r.route.startsWith("/api/")).map((r) => r._sitemap)
|
|
867
|
+
];
|
|
868
|
+
const pageSource = convertNuxtPagesToSitemapEntries(await pagesPromise, {
|
|
869
|
+
isI18nMapped,
|
|
870
|
+
autoLastmod: config.autoLastmod,
|
|
871
|
+
defaultLocale: nuxtI18nConfig.defaultLocale || "en",
|
|
872
|
+
strategy: nuxtI18nConfig.strategy || "no_prefix",
|
|
873
|
+
routesNameSeparator: nuxtI18nConfig.routesNameSeparator,
|
|
874
|
+
normalisedLocales
|
|
875
|
+
});
|
|
876
|
+
if (!resolvedConfigUrls) {
|
|
877
|
+
config.urls && userGlobalSources.push({
|
|
878
|
+
context: {
|
|
879
|
+
name: "sitemap:urls",
|
|
880
|
+
description: "Set with the `sitemap.urls` config."
|
|
881
|
+
},
|
|
882
|
+
urls: await resolveUrls(config.urls)
|
|
883
|
+
});
|
|
884
|
+
resolvedConfigUrls = true;
|
|
885
|
+
}
|
|
886
|
+
const globalSources = [
|
|
887
|
+
...userGlobalSources.map((s) => {
|
|
888
|
+
if (typeof s === "string") {
|
|
889
|
+
return {
|
|
890
|
+
sourceType: "user",
|
|
891
|
+
fetch: s
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
s.sourceType = "user";
|
|
895
|
+
return s;
|
|
896
|
+
}),
|
|
897
|
+
...(config.excludeAppSources === true ? [] : [
|
|
898
|
+
...appGlobalSources,
|
|
899
|
+
{
|
|
900
|
+
context: {
|
|
901
|
+
name: "nuxt:pages",
|
|
902
|
+
description: "Generated from your static page files.",
|
|
903
|
+
tips: [
|
|
904
|
+
"Can be disabled with `{ excludeAppSources: ['nuxt:pages'] }`."
|
|
905
|
+
]
|
|
906
|
+
},
|
|
907
|
+
urls: pageSource
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
context: {
|
|
911
|
+
name: "nuxt:route-rules",
|
|
912
|
+
description: "Generated from your route rules config.",
|
|
913
|
+
tips: [
|
|
914
|
+
"Can be disabled with `{ excludeAppSources: ['nuxt:route-rules'] }`."
|
|
915
|
+
]
|
|
916
|
+
},
|
|
917
|
+
urls: routeRules2
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
context: {
|
|
921
|
+
name: "nuxt:prerender",
|
|
922
|
+
description: "Generated at build time when prerendering.",
|
|
923
|
+
tips: [
|
|
924
|
+
"Can be disabled with `{ excludeAppSources: ['nuxt:prerender'] }`."
|
|
925
|
+
]
|
|
926
|
+
},
|
|
927
|
+
urls: prerenderUrlsFinal
|
|
928
|
+
}
|
|
929
|
+
]).filter((s) => !config.excludeAppSources.includes(s.context.name) && (!!s.urls?.length || !!s.fetch)).map((s) => {
|
|
930
|
+
s.sourceType = "app";
|
|
931
|
+
return s;
|
|
932
|
+
})
|
|
933
|
+
];
|
|
934
|
+
return `export const sources = ${JSON.stringify(globalSources, null, 4)}`;
|
|
935
|
+
};
|
|
936
|
+
const extraSitemapModules = typeof config.sitemaps == "object" ? Object.keys(config.sitemaps).filter((n) => n !== "index") : [];
|
|
937
|
+
const sitemapSources = {};
|
|
938
|
+
nitroConfig.virtual[`#sitemap/child-sources.mjs`] = async () => {
|
|
939
|
+
for (const sitemapName of extraSitemapModules) {
|
|
940
|
+
sitemapSources[sitemapName] = sitemapSources[sitemapName] || [];
|
|
941
|
+
const definition = config.sitemaps[sitemapName];
|
|
942
|
+
if (!sitemapSources[sitemapName].length) {
|
|
943
|
+
definition.urls && sitemapSources[sitemapName].push({
|
|
944
|
+
context: {
|
|
945
|
+
name: `sitemaps:${sitemapName}:urls`,
|
|
946
|
+
description: "Set with the `sitemap.urls` config."
|
|
947
|
+
},
|
|
948
|
+
urls: await resolveUrls(definition.urls)
|
|
949
|
+
});
|
|
950
|
+
definition.dynamicUrlsApiEndpoint && sitemapSources[sitemapName].push({
|
|
951
|
+
context: {
|
|
952
|
+
name: `${sitemapName}:dynamicUrlsApiEndpoint`,
|
|
953
|
+
description: `Generated from your ${sitemapName}:dynamicUrlsApiEndpoint config.`,
|
|
954
|
+
tips: [
|
|
955
|
+
`You should switch to using the \`sitemaps.${sitemapName}.sources\` config which also supports fetch options.`
|
|
956
|
+
]
|
|
957
|
+
},
|
|
958
|
+
fetch: definition.dynamicUrlsApiEndpoint
|
|
959
|
+
});
|
|
960
|
+
sitemapSources[sitemapName].push(
|
|
961
|
+
...(definition.sources || []).map((s) => {
|
|
962
|
+
if (typeof s === "string") {
|
|
963
|
+
return {
|
|
964
|
+
sourceType: "user",
|
|
965
|
+
fetch: s
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
s.sourceType = "user";
|
|
969
|
+
return s;
|
|
970
|
+
})
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return `export const sources = ${JSON.stringify(sitemapSources, null, 4)}`;
|
|
975
|
+
};
|
|
976
|
+
});
|
|
977
|
+
if (config.xsl === "/__sitemap__/style.xsl") {
|
|
978
|
+
addServerHandler({
|
|
979
|
+
route: config.xsl,
|
|
980
|
+
handler: resolve("./runtime/nitro/routes/sitemap.xsl")
|
|
981
|
+
});
|
|
982
|
+
config.xsl = withBase(config.xsl, nuxt.options.app.baseURL);
|
|
983
|
+
if (prerenderSitemap)
|
|
984
|
+
addPrerenderRoutes(config.xsl);
|
|
985
|
+
}
|
|
986
|
+
addServerHandler({
|
|
987
|
+
route: `/${config.sitemapName}`,
|
|
988
|
+
handler: resolve("./runtime/nitro/routes/sitemap.xml")
|
|
989
|
+
});
|
|
990
|
+
setupPrerenderHandler(runtimeConfig);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
export { module as default };
|