import { QueryClient } from '@tanstack/react-query' import { match } from 'path-to-regexp' import { fetchPost, fetchPosts, fetchPostChanges } from '@/lib/posts' import { postsKeys, tagsKeys, wikiKeys } from '@/lib/queryKeys' import { fetchTagByName, fetchTag, fetchTags } from '@/lib/tags' import { fetchWikiPage, fetchWikiPageByTitle, fetchWikiPages } from '@/lib/wiki' import type { Category, FetchPostsOrder, FetchTagsOrder } from '@/types' type Prefetcher = (qc: QueryClient, url: URL) => Promise const mPost = match<{ id: string }> ('/posts/:id') const mWiki = match<{ title: string }> ('/wiki/:title') const prefetchWikiPagesIndex: Prefetcher = async (qc, url) => { const title = url.searchParams.get ('title') ?? '' await qc.prefetchQuery ({ queryKey: wikiKeys.index ({ title }), queryFn: () => fetchWikiPages ({ title }) }) } const prefetchWikiPageShow: Prefetcher = async (qc, url) => { const m = mWiki (url.pathname) if (!(m)) return const title = decodeURIComponent (m.params.title) const version = url.searchParams.get ('version') || undefined const wikiPage = await qc.fetchQuery ({ queryKey: wikiKeys.show (title, { version }), queryFn: () => fetchWikiPageByTitle (title, { version }) }) if (wikiPage) { const effectiveId = String (wikiPage.id ?? '') await qc.prefetchQuery ({ queryKey: wikiKeys.show (effectiveId, { }), queryFn: () => fetchWikiPage (effectiveId, { } ) }) if (wikiPage.body) { await qc.prefetchQuery ({ queryKey: wikiKeys.index ({ }), queryFn: () => fetchWikiPages ({ }) }) } } const effectiveTitle = wikiPage?.title ?? title await qc.prefetchQuery ({ queryKey: tagsKeys.show (effectiveTitle), queryFn: () => fetchTagByName (effectiveTitle) }) if (version) return const p = { tags: effectiveTitle, match: 'all', page: 1, limit: 8, url: '', title: '', originalCreatedFrom: '', originalCreatedTo: '', createdFrom: '', createdTo: '', updatedFrom: '', updatedTo: '', order: 'original_created_at:desc' } as const await qc.prefetchQuery ({ queryKey: postsKeys.index (p), queryFn: () => fetchPosts (p) }) } const prefetchPostsIndex: Prefetcher = async (qc, url) => { const tags = url.searchParams.get ('tags') ?? '' const qURL = url.searchParams.get ('url') ?? '' const title = url.searchParams.get ('title') ?? '' const originalCreatedFrom = url.searchParams.get ('original_created_from') ?? '' const originalCreatedTo = url.searchParams.get ('original_created_to') ?? '' const createdFrom = url.searchParams.get ('created_from') ?? '' const createdTo = url.searchParams.get ('created_to') ?? '' const updatedFrom = url.searchParams.get ('updated_from') ?? '' const updatedTo = url.searchParams.get ('updated_to') ?? '' const m: 'all' | 'any' = url.searchParams.get ('match') === 'any' ? 'any' : 'all' const page = Number (url.searchParams.get ('page') || 1) const limit = Number (url.searchParams.get ('limit') || 20) const order = (url.searchParams.get ('order') ?? 'original_created_at:desc') as FetchPostsOrder const keys = { tags, match: m, page, limit, url: qURL, title, originalCreatedFrom, originalCreatedTo, createdFrom, createdTo, updatedFrom, updatedTo, order } await qc.prefetchQuery ({ queryKey: postsKeys.index (keys), queryFn: () => fetchPosts (keys) }) } const prefetchPostShow: Prefetcher = async (qc, url) => { const m = mPost (url.pathname) if (!(m)) return const { id } = m.params await qc.prefetchQuery ({ queryKey: postsKeys.show (id), queryFn: () => fetchPost (id) }) } const prefetchPostChanges: Prefetcher = async (qc, url) => { const id = url.searchParams.get ('id') const tag = url.searchParams.get ('tag') const page = Number (url.searchParams.get ('page') || 1) const limit = Number (url.searchParams.get ('limit') || 20) if (tag) { await qc.prefetchQuery ({ queryKey: tagsKeys.show (tag), queryFn: () => fetchTag (tag) }) } await qc.prefetchQuery ({ queryKey: postsKeys.changes ({ ...(id && { id }), ...(tag && { tag }), page, limit }), queryFn: () => fetchPostChanges ({ ...(id && { id }), ...(tag && { tag }), page, limit }) }) } const prefetchTagsIndex: Prefetcher = async (qc, url) => { const postRaw = url.searchParams.get ('post') const post = postRaw ? Number (postRaw) : null const name = url.searchParams.get ('name') ?? '' const category = (url.searchParams.get ('category') || null) as Category | null const postCountGTE = Number (url.searchParams.get ('post_count_gte') || 1) const postCountLTERaw = url.searchParams.get ('post_count_lte') const postCountLTE = postCountLTERaw ? Number (postCountLTERaw) : null const createdFrom = url.searchParams.get ('created_from') ?? '' const createdTo = url.searchParams.get ('created_to') ?? '' const updatedFrom = url.searchParams.get ('updated_from') ?? '' const updatedTo = url.searchParams.get ('updated_to') ?? '' const page = Number (url.searchParams.get ('page') || 1) const limit = Number (url.searchParams.get ('limit') || 20) const order = (url.searchParams.get ('order') ?? 'post_count:desc') as FetchTagsOrder const keys = { post, name, category, postCountGTE, postCountLTE, createdFrom, createdTo, updatedFrom, updatedTo, page, limit, order } await qc.prefetchQuery ({ queryKey: tagsKeys.index (keys), queryFn: () => fetchTags (keys) }) } export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[] = [ { test: u => ['/', '/posts', '/posts/search'].includes (u.pathname), run: prefetchPostsIndex }, { test: u => (!(['/posts/new', '/posts/changes', '/posts/search'].includes (u.pathname)) && Boolean (mPost (u.pathname))), run: prefetchPostShow }, { test: u => u.pathname === '/posts/changes', run: prefetchPostChanges }, { test: u => u.pathname === '/wiki', run: prefetchWikiPagesIndex }, { test: u => (!(['/wiki/new', '/wiki/changes'].includes (u.pathname)) && Boolean (mWiki (u.pathname))), run: prefetchWikiPageShow }, { test: u => u.pathname === '/tags', run: prefetchTagsIndex }] export const prefetchForURL = async (qc: QueryClient, urlLike: string): Promise => { const u = new URL (urlLike, location.origin) const r = routePrefetchers.find (x => x.test (u)) if (!(r)) return await r.run (qc, u) }