@nuxtjs/sitemap
Advanced tools
Comparing version 5.3.5 to 6.0.0-beta.1
@@ -1,1 +0,1 @@ | ||
{"id":"ab2ad6a2-f67d-4194-8e33-d46fbc5d1290","timestamp":1721143510557} | ||
{"id":"8204446e-0708-4f8e-bc4a-e52330d54aba","timestamp":1721966466492} |
@@ -8,3 +8,3 @@ { | ||
"configKey": "sitemap", | ||
"version": "5.3.4", | ||
"version": "5.3.5", | ||
"builder": { | ||
@@ -11,0 +11,0 @@ "@nuxt/module-builder": "0.8.1", |
@@ -1,4 +0,5 @@ | ||
export declare function getPathRobotConfigPolyfill(): { | ||
import type { H3Event } from 'h3'; | ||
export declare function getPathRobotConfigPolyfill(e: H3Event, options: any): { | ||
indexable: boolean; | ||
rule: string; | ||
}; |
@@ -1,3 +0,3 @@ | ||
export function getPathRobotConfigPolyfill() { | ||
export function getPathRobotConfigPolyfill(e, options) { | ||
return { indexable: true, rule: "index, follow" }; | ||
} |
@@ -18,2 +18,3 @@ import type { SitemapDefinition } from '../../../types.js'; | ||
isI18nMapped: boolean; | ||
sitemapsPathPrefix: string; | ||
cacheMaxAgeSeconds: number | false; | ||
@@ -20,0 +21,0 @@ sitemapName: string; |
@@ -1,21 +0,24 @@ | ||
import { defineEventHandler, getQuery, setHeader } from "h3"; | ||
import { fixSlashes } from "site-config-stack/urls"; | ||
import { appendHeader, defineEventHandler, setHeader } from "h3"; | ||
import { joinURL } from "ufo"; | ||
import { useSimpleSitemapRuntimeConfig } from "../utils.js"; | ||
import { buildSitemapIndex } from "../sitemap/builder/sitemap-index.js"; | ||
import { createSitePathResolver, useNitroApp, useSiteConfig } from "#imports"; | ||
import { buildSitemapIndex, urlsToIndexXml } from "../sitemap/builder/sitemap-index.js"; | ||
import { useNitroUrlResolvers } from "../sitemap/nitro.js"; | ||
import { useNitroApp } from "#imports"; | ||
export default defineEventHandler(async (e) => { | ||
const canonicalQuery = getQuery(e).canonical; | ||
const isShowingCanonical = typeof canonicalQuery !== "undefined" && canonicalQuery !== "false"; | ||
const runtimeConfig = useSimpleSitemapRuntimeConfig(); | ||
const siteConfig = useSiteConfig(e); | ||
let sitemap = await buildSitemapIndex({ | ||
event: e, | ||
canonicalUrlResolver: createSitePathResolver(e, { canonical: isShowingCanonical || !import.meta.dev, absolute: true, withBase: true }), | ||
relativeBaseUrlResolver: createSitePathResolver(e, { absolute: false, withBase: true }), | ||
fixSlashes: (path) => fixSlashes(siteConfig.trailingSlash, path) | ||
}, runtimeConfig); | ||
const nitro = useNitroApp(); | ||
const ctx = { sitemap, sitemapName: "sitemap" }; | ||
const resolvers = useNitroUrlResolvers(e); | ||
const sitemaps = await buildSitemapIndex(resolvers, runtimeConfig); | ||
if (import.meta.prerender) { | ||
appendHeader( | ||
e, | ||
"x-nitro-prerender", | ||
sitemaps.filter((entry) => !!entry._sitemapName).map((entry) => encodeURIComponent(joinURL(runtimeConfig.sitemapsPathPrefix, `/${entry._sitemapName}.xml`))).join(", ") | ||
); | ||
} | ||
const indexResolvedCtx = { sitemaps }; | ||
await nitro.hooks.callHook("sitemap:index-resolved", indexResolvedCtx); | ||
const output = urlsToIndexXml(indexResolvedCtx.sitemaps, resolvers, runtimeConfig); | ||
const ctx = { sitemap: output, sitemapName: "sitemap" }; | ||
await nitro.hooks.callHook("sitemap:output", ctx); | ||
sitemap = ctx.sitemap; | ||
setHeader(e, "Content-Type", "text/xml; charset=UTF-8"); | ||
@@ -26,3 +29,3 @@ if (runtimeConfig.cacheMaxAgeSeconds) | ||
setHeader(e, "Cache-Control", `no-cache, no-store`); | ||
return sitemap; | ||
return ctx.sitemap; | ||
}); |
@@ -17,3 +17,4 @@ import { defineEventHandler, getHeader, setHeader } from "h3"; | ||
const referrer = getHeader(e, "Referer") || "/"; | ||
const isNotIndexButHasIndex = referrer !== fixPath("/sitemap.xml") && parseURL(referrer).pathname.endsWith("-sitemap.xml"); | ||
const referrerPath = parseURL(referrer).pathname; | ||
const isNotIndexButHasIndex = referrerPath !== "/sitemap.xml" && referrerPath !== "/sitemap_index.xml" && referrerPath.endsWith(".xml"); | ||
const sitemapName = parseURL(referrer).pathname.split("/").pop()?.split("-sitemap")[0] || fallbackSitemapName; | ||
@@ -20,0 +21,0 @@ const title = `${siteName}${sitemapName !== "sitemap.xml" ? ` - ${sitemapName === "sitemap_index.xml" ? "index" : sitemapName}` : ""}`.replace(/&/g, "&"); |
@@ -1,2 +0,3 @@ | ||
import type { ModuleRuntimeConfig, NitroUrlResolvers } from '../../../types.js'; | ||
export declare function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig): Promise<string>; | ||
import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapIndexEntry } from '../../../types.js'; | ||
export declare function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig): Promise<SitemapIndexEntry[]>; | ||
export declare function urlsToIndexXml(sitemaps: SitemapIndexEntry[], resolvers: NitroUrlResolvers, { version, xsl, credits }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits'>): string; |
import { defu } from "defu"; | ||
import { appendHeader } from "h3"; | ||
import { normaliseDate, normaliseSitemapUrls } from "../urlset/normalise.js"; | ||
import { joinURL } from "ufo"; | ||
import { normaliseDate } from "../urlset/normalise.js"; | ||
import { globalSitemapSources, resolveSitemapSources } from "../urlset/sources.js"; | ||
import { applyI18nEnhancements } from "../urlset/i18n.js"; | ||
import { filterSitemapUrls } from "../urlset/filter.js"; | ||
import { sortSitemapUrls } from "../urlset/sort.js"; | ||
import { escapeValueForXml, wrapSitemapXml } from "./xml.js"; | ||
import { useNitroApp } from "#imports"; | ||
import { resolveSitemapEntries } from "./sitemap.js"; | ||
export async function buildSitemapIndex(resolvers, runtimeConfig) { | ||
@@ -20,6 +18,3 @@ const { | ||
sortEntries, | ||
// xls | ||
version, | ||
xsl, | ||
credits | ||
sitemapsPathPrefix | ||
} = runtimeConfig; | ||
@@ -36,8 +31,5 @@ if (!sitemaps) | ||
const sources = await resolveSitemapSources(await globalSitemapSources()); | ||
const normalisedUrls = normaliseSitemapUrls(sources.map((e) => e.urls).flat(), resolvers); | ||
let enhancedUrls = normalisedUrls.map((e) => defu(e, sitemap.defaults)); | ||
if (autoI18n?.locales) | ||
enhancedUrls = applyI18nEnhancements(enhancedUrls, { isI18nMapped, autoI18n, sitemapName: sitemap.sitemapName }); | ||
const filteredUrls = filterSitemapUrls(enhancedUrls, { ...sitemap, autoI18n, isMultiSitemap: true }); | ||
const sortedUrls = maybeSort(filteredUrls); | ||
const normalisedUrls = resolveSitemapEntries(sitemap, sources, { autoI18n, isI18nMapped }); | ||
const enhancedUrls = normalisedUrls.map((e) => defu(e, sitemap.defaults)); | ||
const sortedUrls = maybeSort(enhancedUrls); | ||
sortedUrls.forEach((url, i) => { | ||
@@ -55,9 +47,2 @@ const chunkIndex = Math.floor(i / defaultSitemapsChunkSize); | ||
} | ||
if (import.meta.prerender) { | ||
appendHeader( | ||
resolvers.event, | ||
"x-nitro-prerender", | ||
Object.keys(chunks).map((name) => encodeURIComponent(`/${name}-sitemap.xml`)).join(", ") | ||
); | ||
} | ||
const entries = []; | ||
@@ -67,3 +52,4 @@ for (const name in chunks) { | ||
const entry = { | ||
sitemap: resolvers.canonicalUrlResolver(`${name}-sitemap.xml`) | ||
_sitemapName: name, | ||
sitemap: resolvers.canonicalUrlResolver(joinURL(sitemapsPathPrefix, `/${name}.xml`)) | ||
}; | ||
@@ -82,6 +68,6 @@ let lastmod = sitemap.urls.filter((a) => !!a?.lastmod).map((a) => typeof a.lastmod === "string" ? new Date(a.lastmod) : a.lastmod).sort((a, b) => (b?.getTime() || 0) - (a?.getTime() || 0))?.[0]; | ||
} | ||
const ctx = { sitemaps: entries }; | ||
const nitro = useNitroApp(); | ||
await nitro.hooks.callHook("sitemap:index-resolved", ctx); | ||
const sitemapXml = ctx.sitemaps.map((e) => [ | ||
return entries; | ||
} | ||
export function urlsToIndexXml(sitemaps, resolvers, { version, xsl, credits }) { | ||
const sitemapXml = sitemaps.map((e) => [ | ||
" <sitemap>", | ||
@@ -88,0 +74,0 @@ ` <loc>${escapeValueForXml(e.sitemap)}</loc>`, |
@@ -1,2 +0,9 @@ | ||
import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapDefinition } from '../../../types.js'; | ||
export declare function buildSitemap(sitemap: SitemapDefinition, resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig): Promise<string>; | ||
import type { AutoI18nConfig, ModuleRuntimeConfig, NitroUrlResolvers, ResolvedSitemapUrl, SitemapDefinition, SitemapSourceResolved } from '../../../types.js'; | ||
export interface NormalizedI18n extends ResolvedSitemapUrl { | ||
_pathWithoutPrefix: string; | ||
_locale: AutoI18nConfig['locales'][number]; | ||
_index?: number; | ||
} | ||
export declare function resolveSitemapEntries(sitemap: SitemapDefinition, sources: SitemapSourceResolved[], runtimeConfig: Pick<ModuleRuntimeConfig, 'autoI18n' | 'isI18nMapped'>): ResolvedSitemapUrl[]; | ||
export declare function buildSitemapUrls(sitemap: SitemapDefinition, resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig): Promise<ResolvedSitemapUrl[]>; | ||
export declare function urlsToXml(urls: ResolvedSitemapUrl[], resolvers: NitroUrlResolvers, { version, xsl, credits }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits'>): string; |
@@ -1,15 +0,143 @@ | ||
import { defu } from "defu"; | ||
import { resolveSitePath } from "site-config-stack/urls"; | ||
import { parseURL, withHttps } from "ufo"; | ||
import { normaliseSitemapUrls } from "../urlset/normalise.js"; | ||
import { joinURL, withHttps } from "ufo"; | ||
import { preNormalizeEntry } from "../urlset/normalise.js"; | ||
import { childSitemapSources, globalSitemapSources, resolveSitemapSources } from "../urlset/sources.js"; | ||
import { filterSitemapUrls } from "../urlset/filter.js"; | ||
import { applyI18nEnhancements, normaliseI18nSources } from "../urlset/i18n.js"; | ||
import { sortSitemapUrls } from "../urlset/sort.js"; | ||
import { splitForLocales } from "../../utils.js"; | ||
import { createNitroRouteRuleMatcher } from "../../kit.js"; | ||
import { createPathFilter, logger, splitForLocales } from "../../../utils-pure.js"; | ||
import { handleEntry, wrapSitemapXml } from "./xml.js"; | ||
import { useNitroApp } from "#imports"; | ||
export async function buildSitemap(sitemap, resolvers, runtimeConfig) { | ||
export function resolveSitemapEntries(sitemap, sources, runtimeConfig) { | ||
const { | ||
autoI18n, | ||
isI18nMapped | ||
} = runtimeConfig; | ||
const filterPath = createPathFilter({ | ||
include: sitemap.include, | ||
exclude: sitemap.exclude | ||
}); | ||
const _urls = sources.flatMap((e) => e.urls).map((_e) => { | ||
const e = preNormalizeEntry(_e); | ||
if (!e.loc || !filterPath(e.loc)) | ||
return false; | ||
return e; | ||
}).filter(Boolean); | ||
let validI18nUrlsForTransform = []; | ||
let warnIncorrectI18nTransformUsage = false; | ||
const withoutPrefixPaths = {}; | ||
if (autoI18n && autoI18n.strategy !== "no_prefix") { | ||
const localeCodes = autoI18n.locales.map((l) => l.code); | ||
validI18nUrlsForTransform = _urls.map((_e, i) => { | ||
if (_e._abs) | ||
return false; | ||
const split = splitForLocales(_e.loc, localeCodes); | ||
let localeCode = split[0]; | ||
const pathWithoutPrefix = split[1]; | ||
if (!localeCode) | ||
localeCode = autoI18n.defaultLocale; | ||
const e = _e; | ||
e._pathWithoutPrefix = pathWithoutPrefix; | ||
const locale = autoI18n.locales.find((l) => l.code === localeCode); | ||
if (!locale) | ||
return false; | ||
e._locale = locale; | ||
e._index = i; | ||
withoutPrefixPaths[pathWithoutPrefix] = withoutPrefixPaths[pathWithoutPrefix] || []; | ||
if (!withoutPrefixPaths[pathWithoutPrefix].some((e2) => e2._locale.code === locale.code)) | ||
withoutPrefixPaths[pathWithoutPrefix].push(e); | ||
return e; | ||
}).filter(Boolean); | ||
for (const e of validI18nUrlsForTransform) { | ||
if (!e._i18nTransform && !e.alternatives?.length) { | ||
const alternatives = withoutPrefixPaths[e._pathWithoutPrefix].map((u) => { | ||
const entries = []; | ||
if (u._locale.code === autoI18n.defaultLocale) { | ||
entries.push({ | ||
href: u.loc, | ||
hreflang: "x-default" | ||
}); | ||
} | ||
entries.push({ | ||
href: u.loc, | ||
hreflang: u._locale.code || autoI18n.defaultLocale | ||
}); | ||
return entries; | ||
}).flat().filter(Boolean); | ||
if (alternatives.length) | ||
e.alternatives = alternatives; | ||
} else if (e._i18nTransform) { | ||
delete e._i18nTransform; | ||
if (autoI18n.strategy === "no_prefix") { | ||
warnIncorrectI18nTransformUsage = true; | ||
} | ||
if (autoI18n.differentDomains) { | ||
e.alternatives = [ | ||
{ | ||
// apply default locale domain | ||
...autoI18n.locales.find((l) => [l.code, l.iso].includes(autoI18n.defaultLocale)), | ||
code: "x-default" | ||
}, | ||
...autoI18n.locales.filter((l) => !!l.domain) | ||
].map((locale) => { | ||
return { | ||
hreflang: locale.iso || locale.code, | ||
href: joinURL(withHttps(locale.domain), e._pathWithoutPrefix) | ||
}; | ||
}); | ||
} else { | ||
for (const l of autoI18n.locales) { | ||
let loc = joinURL(`/${l.code}`, e._pathWithoutPrefix); | ||
if (autoI18n.differentDomains || ["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy) && l.code === autoI18n.defaultLocale) | ||
loc = e._pathWithoutPrefix; | ||
const _sitemap = isI18nMapped ? l.iso || l.code : void 0; | ||
const newEntry = preNormalizeEntry({ | ||
_sitemap, | ||
...e, | ||
_index: void 0, | ||
_key: `${_sitemap || ""}${loc}`, | ||
_locale: l, | ||
loc, | ||
alternatives: [{ code: "x-default" }, ...autoI18n.locales].map((locale) => { | ||
const code = locale.code === "x-default" ? autoI18n.defaultLocale : locale.code; | ||
const isDefault = locale.code === "x-default" || locale.code === autoI18n.defaultLocale; | ||
let href = ""; | ||
if (autoI18n.strategy === "prefix") { | ||
href = joinURL("/", code, e._pathWithoutPrefix); | ||
} else if (["prefix_and_default", "prefix_except_default"].includes(autoI18n.strategy)) { | ||
if (isDefault) { | ||
href = e._pathWithoutPrefix; | ||
} else { | ||
href = joinURL("/", code, e._pathWithoutPrefix); | ||
} | ||
} | ||
const hreflang = locale.iso || locale.code; | ||
if (!filterPath(href)) | ||
return false; | ||
return { | ||
hreflang, | ||
href | ||
}; | ||
}).filter(Boolean) | ||
}); | ||
if (e._locale.code === newEntry._locale.code) { | ||
_urls[e._index] = newEntry; | ||
e._index = void 0; | ||
} else { | ||
_urls.push(newEntry); | ||
} | ||
} | ||
} | ||
} | ||
if (isI18nMapped) { | ||
e._sitemap = e._sitemap || e._locale.iso || e._locale.code; | ||
} | ||
if (e._index) | ||
_urls[e._index] = e; | ||
} | ||
} | ||
if (import.meta.dev && warnIncorrectI18nTransformUsage) { | ||
logger.warn("You're using _i18nTransform with the `no_prefix` strategy. This will cause issues with the sitemap. Please remove the _i18nTransform flag or change i18n strategy."); | ||
} | ||
return _urls; | ||
} | ||
export async function buildSitemapUrls(sitemap, resolvers, runtimeConfig) { | ||
const { | ||
sitemaps, | ||
@@ -23,18 +151,14 @@ // enhancing | ||
// chunking | ||
defaultSitemapsChunkSize, | ||
// xls | ||
version, | ||
xsl, | ||
credits | ||
defaultSitemapsChunkSize | ||
} = runtimeConfig; | ||
const isChunking = typeof sitemaps.chunks !== "undefined" && !Number.isNaN(Number(sitemap.sitemapName)); | ||
function maybeSort(urls2) { | ||
return sortEntries ? sortSitemapUrls(urls2) : urls2; | ||
function maybeSort(urls) { | ||
return sortEntries ? sortSitemapUrls(urls) : urls; | ||
} | ||
function maybeSlice(urls2) { | ||
function maybeSlice(urls) { | ||
if (isChunking && defaultSitemapsChunkSize) { | ||
const chunk = Number(sitemap.sitemapName); | ||
return urls2.slice(chunk * defaultSitemapsChunkSize, (chunk + 1) * defaultSitemapsChunkSize); | ||
return urls.slice(chunk * defaultSitemapsChunkSize, (chunk + 1) * defaultSitemapsChunkSize); | ||
} | ||
return urls2; | ||
return urls; | ||
} | ||
@@ -56,37 +180,13 @@ if (autoI18n?.differentDomains) { | ||
sources.push(...await childSitemapSources(sitemap)); | ||
let resolvedSources = await resolveSitemapSources(sources, resolvers.event); | ||
if (autoI18n) | ||
resolvedSources = normaliseI18nSources(resolvedSources, { autoI18n, isI18nMapped, ...sitemap }); | ||
const normalisedUrls = normaliseSitemapUrls(resolvedSources.map((e) => e.urls).flat(), resolvers); | ||
const routeRuleMatcher = createNitroRouteRuleMatcher(); | ||
let enhancedUrls = normalisedUrls.map((e) => defu(e, sitemap.defaults)).map((e) => { | ||
const path = parseURL(e.loc).pathname; | ||
let routeRules = routeRuleMatcher(path); | ||
if (autoI18n?.locales && autoI18n?.strategy !== "no_prefix") { | ||
const match = splitForLocales(path, autoI18n.locales.map((l) => l.code)); | ||
const pathWithoutPrefix = match[1]; | ||
if (pathWithoutPrefix && pathWithoutPrefix !== path) | ||
routeRules = defu(routeRules, routeRuleMatcher(pathWithoutPrefix)); | ||
} | ||
if (routeRules.sitemap === false) | ||
return false; | ||
if (typeof routeRules.index !== "undefined" && !routeRules.index) | ||
return false; | ||
const hasRobotsDisabled = Object.entries(routeRules.headers || {}).some(([name, value]) => name.toLowerCase() === "x-robots-tag" && value.toLowerCase() === "noindex"); | ||
if (routeRules.redirect || hasRobotsDisabled) | ||
return false; | ||
return routeRules.sitemap ? defu(e, routeRules.sitemap) : e; | ||
}).filter(Boolean); | ||
if (autoI18n?.locales) | ||
enhancedUrls = applyI18nEnhancements(enhancedUrls, { isI18nMapped, autoI18n, ...sitemap }); | ||
const filteredUrls = filterSitemapUrls(enhancedUrls, { event: resolvers.event, isMultiSitemap, autoI18n, ...sitemap }); | ||
const resolvedSources = await resolveSitemapSources(sources, resolvers.event); | ||
const enhancedUrls = resolveSitemapEntries(sitemap, resolvedSources, { autoI18n, isI18nMapped }); | ||
const filteredUrls = enhancedUrls.filter((e) => { | ||
if (isMultiSitemap && e._sitemap && sitemap.sitemapName) | ||
return e._sitemap === sitemap.sitemapName; | ||
return true; | ||
}); | ||
const sortedUrls = maybeSort(filteredUrls); | ||
const slicedUrls = maybeSlice(sortedUrls); | ||
const nitro = useNitroApp(); | ||
const ctx = { | ||
urls: slicedUrls, | ||
sitemapName: sitemap.sitemapName | ||
}; | ||
await nitro.hooks.callHook("sitemap:resolved", ctx); | ||
const urls = maybeSort(normaliseSitemapUrls(ctx.urls, resolvers)); | ||
return maybeSlice(sortedUrls); | ||
} | ||
export function urlsToXml(urls, resolvers, { version, xsl, credits }) { | ||
const urlset = urls.map((e) => { | ||
@@ -93,0 +193,0 @@ const keys = Object.keys(e).filter((k) => !k.startsWith("_")); |
import type { H3Event } from 'h3'; | ||
import type { ModuleRuntimeConfig, NitroUrlResolvers, SitemapDefinition } from '../../types.js'; | ||
export declare function useNitroUrlResolvers(e: H3Event): NitroUrlResolvers; | ||
export declare function createSitemap(e: H3Event, definition: SitemapDefinition, runtimeConfig: ModuleRuntimeConfig): Promise<string>; | ||
export declare function createSitemap(event: H3Event, definition: SitemapDefinition, runtimeConfig: ModuleRuntimeConfig): Promise<string>; |
import { getQuery, setHeader } from "h3"; | ||
import { fixSlashes } from "site-config-stack/urls"; | ||
import { buildSitemap } from "./builder/sitemap.js"; | ||
import { buildSitemapIndex } from "./builder/sitemap-index.js"; | ||
import { createSitePathResolver, useNitroApp, useSiteConfig } from "#imports"; | ||
import { defu } from "defu"; | ||
import { mergeOnKey, splitForLocales } from "../../utils-pure.js"; | ||
import { createNitroRouteRuleMatcher } from "../kit.js"; | ||
import { buildSitemapUrls, urlsToXml } from "./builder/sitemap.js"; | ||
import { normaliseEntry } from "./urlset/normalise.js"; | ||
import { sortSitemapUrls } from "./urlset/sort.js"; | ||
import { createSitePathResolver, getPathRobotConfig, useNitroApp, useSiteConfig } from "#imports"; | ||
export function useNitroUrlResolvers(e) { | ||
@@ -22,16 +26,47 @@ const canonicalQuery = getQuery(e).canonical; | ||
} | ||
export async function createSitemap(e, definition, runtimeConfig) { | ||
export async function createSitemap(event, definition, runtimeConfig) { | ||
const { sitemapName } = definition; | ||
const nitro = useNitroApp(); | ||
let sitemap = await (definition.sitemapName === "index" ? buildSitemapIndex(useNitroUrlResolvers(e), runtimeConfig) : buildSitemap(definition, useNitroUrlResolvers(e), runtimeConfig)); | ||
const resolvers = useNitroUrlResolvers(event); | ||
let sitemapUrls = await buildSitemapUrls(definition, resolvers, runtimeConfig); | ||
const routeRuleMatcher = createNitroRouteRuleMatcher(); | ||
const { autoI18n } = runtimeConfig; | ||
sitemapUrls = sitemapUrls.map((u) => { | ||
const path = u._path?.pathname || u.loc; | ||
if (!getPathRobotConfig(event, { path, skipSiteIndexable: true }).indexable) | ||
return false; | ||
let routeRules = routeRuleMatcher(path); | ||
if (autoI18n?.locales && autoI18n?.strategy !== "no_prefix") { | ||
const match = splitForLocales(path, autoI18n.locales.map((l) => l.code)); | ||
const pathWithoutPrefix = match[1]; | ||
if (pathWithoutPrefix && pathWithoutPrefix !== path) | ||
routeRules = defu(routeRules, routeRuleMatcher(pathWithoutPrefix)); | ||
} | ||
if (routeRules.sitemap === false) | ||
return false; | ||
if (typeof routeRules.index !== "undefined" && !routeRules.index || typeof routeRules.robots !== "undefined" && !routeRules.robots) { | ||
return false; | ||
} | ||
const hasRobotsDisabled = Object.entries(routeRules.headers || {}).some(([name, value]) => name.toLowerCase() === "x-robots-tag" && value.toLowerCase().includes("noindex")); | ||
if (routeRules.redirect || hasRobotsDisabled) | ||
return false; | ||
return routeRules.sitemap ? defu(u, routeRules.sitemap) : u; | ||
}).filter(Boolean); | ||
const resolvedCtx = { | ||
urls: sitemapUrls, | ||
sitemapName | ||
}; | ||
await nitro.hooks.callHook("sitemap:resolved", resolvedCtx); | ||
const maybeSort = (urls2) => runtimeConfig.sortEntries ? sortSitemapUrls(urls2) : urls2; | ||
const urls = maybeSort(mergeOnKey(resolvedCtx.urls.map((e) => normaliseEntry(e, definition.defaults, resolvers)), "_key")); | ||
const sitemap = urlsToXml(urls, resolvers, runtimeConfig); | ||
const ctx = { sitemap, sitemapName }; | ||
await nitro.hooks.callHook("sitemap:output", ctx); | ||
sitemap = ctx.sitemap; | ||
setHeader(e, "Content-Type", "text/xml; charset=UTF-8"); | ||
setHeader(event, "Content-Type", "text/xml; charset=UTF-8"); | ||
if (runtimeConfig.cacheMaxAgeSeconds) | ||
setHeader(e, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, must-revalidate`); | ||
setHeader(event, "Cache-Control", `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, must-revalidate`); | ||
else | ||
setHeader(e, "Cache-Control", `no-cache, no-store`); | ||
e.context._isSitemap = true; | ||
return sitemap; | ||
setHeader(event, "Cache-Control", `no-cache, no-store`); | ||
event.context._isSitemap = true; | ||
return ctx.sitemap; | ||
} |
@@ -1,4 +0,5 @@ | ||
import type { NitroUrlResolvers, ResolvedSitemapUrl, SitemapUrlInput } from '../../../types.js'; | ||
export declare function normaliseSitemapUrls(data: SitemapUrlInput[], resolvers: NitroUrlResolvers): ResolvedSitemapUrl[]; | ||
import type { NitroUrlResolvers, ResolvedSitemapUrl, SitemapUrl } from '../../../types.js'; | ||
export declare function preNormalizeEntry(_e: SitemapUrl | string): ResolvedSitemapUrl; | ||
export declare function normaliseEntry(_e: ResolvedSitemapUrl, defaults: Omit<SitemapUrl, 'loc'>, resolvers?: NitroUrlResolvers): ResolvedSitemapUrl; | ||
export declare function isValidW3CDate(d: string): boolean; | ||
export declare function normaliseDate(date: string | Date): string; |
@@ -1,6 +0,6 @@ | ||
import { hasProtocol } from "ufo"; | ||
import { fixSlashes } from "site-config-stack/urls"; | ||
import { hasProtocol, parsePath, parseURL } from "ufo"; | ||
import { defu } from "defu"; | ||
import { mergeOnKey } from "../../../utils-pure.js"; | ||
function resolve(s, resolvers) { | ||
if (typeof s === "undefined") | ||
if (typeof s === "undefined" || !resolvers) | ||
return s; | ||
@@ -12,54 +12,65 @@ s = typeof s === "string" ? s : s.toString(); | ||
} | ||
export function normaliseSitemapUrls(data, resolvers) { | ||
const entries = data.map((e) => typeof e === "string" ? { loc: e } : e).map((e) => { | ||
e = { ...e }; | ||
if (e.url) { | ||
e.loc = e.url; | ||
delete e.url; | ||
} | ||
e.loc = fixSlashes(false, e.loc); | ||
return e; | ||
}).filter(Boolean); | ||
function normaliseEntry(e) { | ||
if (e.lastmod) { | ||
const date = normaliseDate(e.lastmod); | ||
if (date) | ||
e.lastmod = date; | ||
else | ||
delete e.lastmod; | ||
} | ||
if (!e.lastmod) | ||
function removeTrailingSlash(s) { | ||
return s.replace(/\/(\?|#|$)/, "$1"); | ||
} | ||
export function preNormalizeEntry(_e) { | ||
const e = typeof _e === "string" ? { loc: _e } : { ..._e }; | ||
if (e.url && !e.loc) { | ||
e.loc = e.url; | ||
delete e.url; | ||
} | ||
e.loc = removeTrailingSlash(e.loc || ""); | ||
e._abs = hasProtocol(e.loc, { acceptRelative: false, strict: false }); | ||
try { | ||
e._path = e._abs ? parseURL(e.loc) : parsePath(e.loc); | ||
} catch (e2) { | ||
e2._path = null; | ||
} | ||
if (e._path?.pathname === "") | ||
e.loc = `${e.loc}/`; | ||
if (e._path) { | ||
e._key = `${e._sitemap || ""}${e._path?.pathname || "/"}${e._path.search}`; | ||
} else { | ||
e._key = e.loc; | ||
} | ||
return e; | ||
} | ||
export function normaliseEntry(_e, defaults, resolvers) { | ||
const e = defu(_e, defaults); | ||
if (e.lastmod) { | ||
const date = normaliseDate(e.lastmod); | ||
if (date) | ||
e.lastmod = date; | ||
else | ||
delete e.lastmod; | ||
e.loc = resolve(e.loc, resolvers); | ||
if (e.alternatives) { | ||
e.alternatives = mergeOnKey(e.alternatives.map((e2) => { | ||
const a = { ...e2 }; | ||
if (typeof a.href === "string") | ||
a.href = resolve(a.href, resolvers); | ||
else if (typeof a.href === "object" && a.href) | ||
a.href = resolve(a.href.href, resolvers); | ||
return a; | ||
}), "hreflang"); | ||
} | ||
if (e.images) { | ||
e.images = mergeOnKey(e.images.map((i) => { | ||
i = { ...i }; | ||
i.loc = resolve(i.loc, resolvers); | ||
return i; | ||
}), "loc"); | ||
} | ||
if (e.videos) { | ||
e.videos = e.videos.map((v) => { | ||
v = { ...v }; | ||
if (v.content_loc) | ||
v.content_loc = resolve(v.content_loc, resolvers); | ||
return v; | ||
}); | ||
} | ||
return e; | ||
} | ||
return mergeOnKey( | ||
entries.map(normaliseEntry).map((e) => ({ ...e, _key: `${e._sitemap || ""}${e.loc}` })), | ||
"_key" | ||
); | ||
if (!e.lastmod) | ||
delete e.lastmod; | ||
e.loc = resolve(e.loc, resolvers); | ||
if (e.alternatives) { | ||
e.alternatives = mergeOnKey(e.alternatives.map((e2) => { | ||
const a = { ...e2 }; | ||
if (typeof a.href === "string") | ||
a.href = resolve(a.href, resolvers); | ||
else if (typeof a.href === "object" && a.href) | ||
a.href = resolve(a.href.href, resolvers); | ||
return a; | ||
}), "hreflang"); | ||
} | ||
if (e.images) { | ||
e.images = mergeOnKey(e.images.map((i) => { | ||
i = { ...i }; | ||
i.loc = resolve(i.loc, resolvers); | ||
return i; | ||
}), "loc"); | ||
} | ||
if (e.videos) { | ||
e.videos = e.videos.map((v) => { | ||
v = { ...v }; | ||
if (v.content_loc) | ||
v.content_loc = resolve(v.content_loc, resolvers); | ||
return v; | ||
}); | ||
} | ||
return e; | ||
} | ||
@@ -66,0 +77,0 @@ const IS_VALID_W3C_DATE = [ |
import type { FetchOptions } from 'ofetch'; | ||
import type { H3Event } from 'h3'; | ||
import type { ParsedURL } from 'ufo'; | ||
export interface ModuleOptions extends SitemapDefinition { | ||
@@ -43,2 +44,8 @@ /** | ||
/** | ||
* The path prefix for the sitemaps. | ||
* | ||
* @default /__sitemap__/ | ||
*/ | ||
sitemapsPathPrefix: string; | ||
/** | ||
* Sitemaps to append to the sitemap index. | ||
@@ -197,3 +204,3 @@ * | ||
} | ||
export interface ModuleRuntimeConfig extends Pick<ModuleOptions, 'cacheMaxAgeSeconds' | 'sitemapName' | 'excludeAppSources' | 'sortEntries' | 'defaultSitemapsChunkSize' | 'xslColumns' | 'xslTips' | 'debug' | 'discoverImages' | 'discoverVideos' | 'autoLastmod' | 'xsl' | 'credits'> { | ||
export interface ModuleRuntimeConfig extends Pick<ModuleOptions, 'sitemapsPathPrefix' | 'cacheMaxAgeSeconds' | 'sitemapName' | 'excludeAppSources' | 'sortEntries' | 'defaultSitemapsChunkSize' | 'xslColumns' | 'xslTips' | 'debug' | 'discoverImages' | 'discoverVideos' | 'autoLastmod' | 'xsl' | 'credits'> { | ||
version: string; | ||
@@ -215,2 +222,6 @@ isNuxtContentDocumentDriven: boolean; | ||
lastmod?: string; | ||
/** | ||
* @internal | ||
*/ | ||
_sitemapName?: string; | ||
} | ||
@@ -220,3 +231,16 @@ export type FilterInput = (string | RegExp | { | ||
}); | ||
export type ResolvedSitemapUrl = Omit<SitemapUrl, 'url'> & Required<Pick<SitemapUrl, 'loc'>>; | ||
export type ResolvedSitemapUrl = Omit<SitemapUrl, 'url'> & Required<Pick<SitemapUrl, 'loc'>> & { | ||
/** | ||
* @internal | ||
*/ | ||
_key: string; | ||
/** | ||
* @internal | ||
*/ | ||
_path: ParsedURL; | ||
/** | ||
* @internal | ||
*/ | ||
_abs: boolean; | ||
}; | ||
export interface SitemapDefinition { | ||
@@ -223,0 +247,0 @@ /** |
import type { FilterInput } from './types.js'; | ||
export declare const logger: import("consola").ConsolaInstance; | ||
export declare function mergeOnKey<T, K extends keyof T>(arr: T[], key: K): T[]; | ||
export declare function splitForLocales(path: string, locales: string[]): (string | null)[]; | ||
export declare function splitForLocales(path: string, locales: string[]): [string | null, string]; | ||
/** | ||
@@ -5,0 +6,0 @@ * Transform a literal notation string regex to RegExp |
import { createDefu } from "defu"; | ||
import { parseURL, withLeadingSlash } from "ufo"; | ||
import { createRouter, toRouteMatcher } from "radix3"; | ||
import { createConsola } from "consola"; | ||
export const logger = createConsola({ | ||
defaults: { | ||
tag: "@nuxt/sitemap" | ||
} | ||
}); | ||
const merger = createDefu((obj, key, value) => { | ||
@@ -5,0 +11,0 @@ if (Array.isArray(obj[key]) && Array.isArray(value)) |
{ | ||
"name": "@nuxtjs/sitemap", | ||
"type": "module", | ||
"version": "5.3.5", | ||
"version": "6.0.0-beta.1", | ||
"description": "Powerfully flexible XML Sitemaps that integrate seamlessly, for Nuxt.", | ||
@@ -67,3 +67,2 @@ "author": { | ||
"nuxt": "^3.12.3", | ||
"nuxt-icon": "1.0.0-beta.7", | ||
"typescript": "5.4.5", | ||
@@ -86,6 +85,5 @@ "vitest": "^2.0.3" | ||
"build": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build && npm run client:build", | ||
"dev": "nuxi dev .playground", | ||
"dev:devtool": "nuxi dev .playground-devtools", | ||
"dev:build": "nuxi build .playground", | ||
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare .playground", | ||
"dev": "nuxi dev playground", | ||
"dev:build": "nuxi build playground", | ||
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground", | ||
"release": "pnpm build && bumpp && pnpm -r publish", | ||
@@ -92,0 +90,0 @@ "test": "vitest" |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
15
1
8732763
322
50435
1