Skip to content

Commit

Permalink
perf!: rewrite i18n resolving and url normalizing (#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
harlan-zw authored Jul 21, 2024
1 parent b2d1409 commit fab7e9e
Show file tree
Hide file tree
Showing 25 changed files with 845 additions and 590 deletions.
40 changes: 23 additions & 17 deletions src/runtime/nitro/routes/sitemap_index.xml.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
import { defineEventHandler, getQuery, setHeader } from 'h3'
import { fixSlashes } from 'site-config-stack/urls'
import { appendHeader, defineEventHandler, setHeader } from 'h3'
import { useSimpleSitemapRuntimeConfig } from '../utils'
import { buildSitemapIndex } from '../sitemap/builder/sitemap-index'
import { buildSitemapIndex, urlsToIndexXml } from '../sitemap/builder/sitemap-index'
import type { SitemapOutputHookCtx } from '../../types'
import { createSitePathResolver, useNitroApp, useSiteConfig } from '#imports'
import { useNitroUrlResolvers } from '..//sitemap/nitro'
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: string) => fixSlashes(siteConfig.trailingSlash, path),
}, runtimeConfig))

const nitro = useNitroApp()
const resolvers = useNitroUrlResolvers(e)
const sitemaps = (await buildSitemapIndex(resolvers, runtimeConfig))

// tell the prerender to render the other sitemaps (if we prerender this one)
// this solves the dynamic chunking sitemap issue
if (import.meta.prerender) {
appendHeader(
e,
'x-nitro-prerender',
sitemaps.filter(entry => !!entry._sitemapName)
.map(entry => encodeURIComponent(`/${entry._sitemapName}-sitemap.xml`)).join(', '),
)
}

const indexResolvedCtx = { sitemaps }
await nitro.hooks.callHook('sitemap:index-resolved', indexResolvedCtx)

const ctx: SitemapOutputHookCtx = { sitemap, sitemapName: 'sitemap' }
const output = urlsToIndexXml(indexResolvedCtx.sitemaps, resolvers, runtimeConfig)
const ctx: SitemapOutputHookCtx = { sitemap: output, sitemapName: 'sitemap' }
await nitro.hooks.callHook('sitemap:output', ctx)
sitemap = ctx.sitemap

setHeader(e, 'Content-Type', 'text/xml; charset=UTF-8')
if (runtimeConfig.cacheMaxAgeSeconds)
setHeader(e, 'Cache-Control', `public, max-age=${runtimeConfig.cacheMaxAgeSeconds}, must-revalidate`)
else
setHeader(e, 'Cache-Control', `no-cache, no-store`)
return sitemap
return ctx.sitemap
})
45 changes: 10 additions & 35 deletions src/runtime/nitro/sitemap/builder/sitemap-index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import { defu } from 'defu'
import { appendHeader } from 'h3'
import type {
ModuleRuntimeConfig,
NitroUrlResolvers,
ResolvedSitemapUrl,
SitemapIndexEntry,
SitemapUrl,
} from '../../../types'
import { normaliseDate, normaliseSitemapUrls } from '../urlset/normalise'
import { normaliseDate } from '../urlset/normalise'
import { globalSitemapSources, resolveSitemapSources } from '../urlset/sources'
import { applyI18nEnhancements } from '../urlset/i18n'
import { filterSitemapUrls } from '../urlset/filter'
import { sortSitemapUrls } from '../urlset/sort'
import { escapeValueForXml, wrapSitemapXml } from './xml'
import { useNitroApp } from '#imports'
import { resolveSitemapEntries } from './sitemap'

export async function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeConfig: ModuleRuntimeConfig) {
const {
Expand All @@ -25,10 +22,6 @@ export async function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeCon
autoI18n,
isI18nMapped,
sortEntries,
// xls
version,
xsl,
credits,
} = runtimeConfig

if (!sitemaps)
Expand All @@ -42,22 +35,13 @@ export async function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeCon
const chunks: Record<string | number, { urls: SitemapUrl[] }> = {}
if (isChunking) {
const sitemap = sitemaps.chunks
// TODO
// we need to figure out how many entries we're dealing with
const sources = await resolveSitemapSources(await globalSitemapSources())
// we need to generate multiple sitemaps with dynamically generated names
const normalisedUrls = normaliseSitemapUrls(sources.map(e => e.urls).flat(), resolvers)
const normalisedUrls = resolveSitemapEntries(sitemap, sources, { autoI18n, isI18nMapped })
// 2. enhance
let enhancedUrls: ResolvedSitemapUrl[] = normalisedUrls
const enhancedUrls: ResolvedSitemapUrl[] = normalisedUrls
.map(e => defu(e, sitemap.defaults) as ResolvedSitemapUrl)
// TODO enable
if (autoI18n?.locales)
enhancedUrls = applyI18nEnhancements(enhancedUrls, { isI18nMapped, autoI18n, sitemapName: sitemap.sitemapName })
// 3. filtered urls
// TODO make sure include and exclude start with baseURL?
const filteredUrls = filterSitemapUrls(enhancedUrls, { ...sitemap, autoI18n, isMultiSitemap: true })
// 4. sort
const sortedUrls = maybeSort(filteredUrls)
const sortedUrls = maybeSort(enhancedUrls)
// split into the max size which should be 1000
sortedUrls.forEach((url, i) => {
const chunkIndex = Math.floor(i / (defaultSitemapsChunkSize as number))
Expand All @@ -74,21 +58,12 @@ export async function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeCon
}
}

// tell the prerender to render the other sitemaps (if we prerender this one)
// this solves the dynamic chunking sitemap issue
if (import.meta.prerender) {
appendHeader(
resolvers.event,
'x-nitro-prerender',
Object.keys(chunks).map(name => encodeURIComponent(`/${name}-sitemap.xml`)).join(', '),
)
}

const entries: SitemapIndexEntry[] = []
// normalise
for (const name in chunks) {
const sitemap = chunks[name]
const entry: SitemapIndexEntry = {
_sitemapName: name,
sitemap: resolvers.canonicalUrlResolver(`${name}-sitemap.xml`),
}
let lastmod = sitemap.urls
Expand All @@ -110,11 +85,11 @@ export async function buildSitemapIndex(resolvers: NitroUrlResolvers, runtimeCon
}))
}

const ctx = { sitemaps: entries }
const nitro = useNitroApp()
await nitro.hooks.callHook('sitemap:index-resolved', ctx)
return entries
}

const sitemapXml = ctx.sitemaps.map(e => [
export function urlsToIndexXml(sitemaps: SitemapIndexEntry[], resolvers: NitroUrlResolvers, { version, xsl, credits }: Pick<ModuleRuntimeConfig, 'version' | 'xsl' | 'credits'>) {
const sitemapXml = sitemaps.map(e => [
' <sitemap>',
` <loc>${escapeValueForXml(e.sitemap)}</loc>`,
// lastmod is optional
Expand Down
Loading

0 comments on commit fab7e9e

Please sign in to comment.