mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-02-04 06:25:41 -06:00
Merge 50ce65a2b4 into ec00a40aef
This commit is contained in:
commit
8005a9367f
@ -2,7 +2,15 @@ import { Root } from "hast"
|
||||
import { GlobalConfiguration } from "../../cfg"
|
||||
import { getDate } from "../../components/Date"
|
||||
import { escapeHTML } from "../../util/escape"
|
||||
import { FilePath, FullSlug, SimpleSlug, joinSegments, simplifySlug } from "../../util/path"
|
||||
import {
|
||||
FilePath,
|
||||
FullSlug,
|
||||
SimpleSlug,
|
||||
getAllSegmentPrefixes,
|
||||
joinSegments,
|
||||
simplifySlug,
|
||||
slugTag,
|
||||
} from "../../util/path"
|
||||
import { QuartzEmitterPlugin } from "../types"
|
||||
import { toHtml } from "hast-util-to-html"
|
||||
import { write } from "./helpers"
|
||||
@ -28,6 +36,9 @@ interface Options {
|
||||
rssFullHtml: boolean
|
||||
rssSlug: string
|
||||
includeEmptyFiles: boolean
|
||||
includeTags: boolean
|
||||
rssTagsLimit: number
|
||||
rssTags: string[]
|
||||
}
|
||||
|
||||
const defaultOptions: Options = {
|
||||
@ -37,6 +48,9 @@ const defaultOptions: Options = {
|
||||
rssFullHtml: false,
|
||||
rssSlug: "index",
|
||||
includeEmptyFiles: true,
|
||||
includeTags: false,
|
||||
rssTagsLimit: 15,
|
||||
rssTags: [],
|
||||
}
|
||||
|
||||
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndexMap): string {
|
||||
@ -135,6 +149,62 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
||||
slug: (opts?.rssSlug ?? "index") as FullSlug,
|
||||
ext: ".xml",
|
||||
})
|
||||
|
||||
if (opts?.includeTags) {
|
||||
// Optimization: Build a map of tag -> content list once (reverse index)
|
||||
const tagsInput: Map<string, ContentDetails[]> = new Map()
|
||||
|
||||
// Iterate over the content index once to populate the reverse index
|
||||
for (const [_, content] of linkIndex) {
|
||||
const tags = content.tags.flatMap(getAllSegmentPrefixes)
|
||||
for (const tag of new Set(tags)) {
|
||||
// Use Set to avoid double counting per file
|
||||
if (!tagsInput.has(tag)) {
|
||||
tagsInput.set(tag, [])
|
||||
}
|
||||
tagsInput.get(tag)!.push(content)
|
||||
}
|
||||
}
|
||||
|
||||
let sortedTags: string[] = []
|
||||
|
||||
if (opts.rssTags && opts.rssTags.length > 0) {
|
||||
// Deduplicate and slugify user-provided tags
|
||||
const userTags = new Set(opts.rssTags.map((tag) => slugTag(tag)))
|
||||
|
||||
// Filter user tags to only those that exist in the content
|
||||
sortedTags = Array.from(userTags).filter((tag) => tagsInput.has(tag))
|
||||
} else if ((opts.rssTagsLimit ?? 0) > 0) {
|
||||
// Sort available tags by frequency (number of content items)
|
||||
sortedTags = Array.from(tagsInput.entries())
|
||||
.sort((a, b) => b[1].length - a[1].length) // Sort by frequency descending
|
||||
.slice(0, opts.rssTagsLimit)
|
||||
.map(([tag]) => tag)
|
||||
}
|
||||
|
||||
if (sortedTags.length === 0) {
|
||||
console.warn(
|
||||
"[contentIndex] includeTags is enabled, but no tag-based RSS feeds will be generated. " +
|
||||
"Either provide non-empty `rssTags` matching content tags or set `rssTagsLimit` to a positive number.",
|
||||
)
|
||||
}
|
||||
|
||||
for (const tag of sortedTags) {
|
||||
const tagContent = tagsInput.get(tag)
|
||||
if (!tagContent) continue // Should not happen given logic above
|
||||
|
||||
// Reconstruct a map for generateRSSFeed (it expects a ContentIndexMap)
|
||||
// We can optimize this by making generateRSSFeed accept an array, but for now we conform to the interface
|
||||
const tagFilteredIndex = new Map(tagContent.map((content) => [content.slug, content]))
|
||||
|
||||
yield write({
|
||||
ctx,
|
||||
content: generateRSSFeed(cfg, tagFilteredIndex, opts.rssLimit),
|
||||
slug: joinSegments("tags", tag, "index") as FullSlug,
|
||||
ext: ".xml",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fp = joinSegments("static", "contentIndex") as FullSlug
|
||||
|
||||
Loading…
Reference in New Issue
Block a user