first draft at unlisted files

This commit is contained in:
Harv 2025-10-15 20:26:33 +00:00 committed by GitHub
parent b4805a1031
commit d446ee1f7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 56 additions and 9 deletions

View File

@ -18,6 +18,7 @@ const config: QuartzConfig = {
locale: "en-US",
baseUrl: "quartz.jzhao.xyz",
ignorePatterns: ["private", "templates", ".obsidian"],
unlistedPatterns: [],
defaultDateType: "modified",
theme: {
fontOrigin: "googleFonts",

View File

@ -62,6 +62,8 @@ export interface GlobalConfiguration {
analytics: Analytics
/** Glob patterns to not search */
ignorePatterns: string[]
/** Glob patterns to mark files as unlisted (hidden from listings but still accessible via direct link) */
unlistedPatterns?: string[]
/** Whether to use created, modified, or published as the default type of date */
defaultDateType: ValidDateType
/** Base URL to use for CNAME files, sitemaps, and RSS feeds that require an absolute URL.

View File

@ -50,7 +50,7 @@ export default ((opts?: Partial<BreadcrumbOptions>) => {
displayClass,
ctx,
}: QuartzComponentProps) => {
const trie = (ctx.trie ??= trieFromAllFiles(allFiles))
const trie = (ctx.trie ??= trieFromAllFiles(allFiles, ctx.cfg))
const slugParts = fileData.slug!.split("/")
const pathNodes = trie.ancestryChain(slugParts)

View File

@ -30,7 +30,7 @@ export default ((opts?: Partial<FolderContentOptions>) => {
const FolderContent: QuartzComponent = (props: QuartzComponentProps) => {
const { tree, fileData, allFiles, cfg } = props
const trie = (props.ctx.trie ??= trieFromAllFiles(allFiles))
const trie = (props.ctx.trie ??= trieFromAllFiles(allFiles, props.ctx.cfg))
const folder = trie.findNode(fileData.slug!.split("/"))
if (!folder) {
return null

View File

@ -7,6 +7,7 @@ import { QuartzEmitterPlugin } from "../types"
import { toHtml } from "hast-util-to-html"
import { write } from "./helpers"
import { i18n } from "../../i18n"
import { isUnlisted } from "../filters/unlisted"
export type ContentIndexMap = Map<FullSlug, ContentDetails>
export type ContentDetails = {
@ -102,6 +103,11 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
for (const [tree, file] of content) {
const slug = file.data.slug!
const date = getDate(ctx.cfg.configuration, file.data) ?? new Date()
if(isUnlisted(file.data, cfg)) {
continue
}
if (opts?.includeEmptyFiles || (file.data.text && file.data.text !== "")) {
linkIndex.set(slug, {
slug,

View File

@ -14,6 +14,7 @@ import { BuildCtx } from "../../util/ctx"
import { Node } from "unist"
import { StaticResources } from "../../util/resources"
import { QuartzPluginData } from "../vfile"
import { isUnlisted } from "../filters/unlisted"
async function processContent(
ctx: BuildCtx,
@ -74,7 +75,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
]
},
async *emit(ctx, content, resources) {
const allFiles = content.map((c) => c[1].data)
const allFiles = content.map((c) => c[1].data).filter(f => !isUnlisted(f, ctx.cfg.configuration))
let containsIndex = false
for (const [tree, file] of content) {
@ -98,7 +99,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
}
},
async *partialEmit(ctx, content, resources, changeEvents) {
const allFiles = content.map((c) => c[1].data)
const allFiles = content.map((c) => c[1].data).filter(f => !isUnlisted(f, ctx.cfg.configuration))
// find all slugs that changed or were added
const changedSlugs = new Set<string>()

View File

@ -20,6 +20,7 @@ import { write } from "./helpers"
import { i18n, TRANSLATIONS } from "../../i18n"
import { BuildCtx } from "../../util/ctx"
import { StaticResources } from "../../util/resources"
import { isUnlisted } from "../filters/unlisted"
interface FolderPageOptions extends FullPageLayout {
sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number
}
@ -129,7 +130,7 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FolderPageOptions>> = (user
]
},
async *emit(ctx, content, resources) {
const allFiles = content.map((c) => c[1].data)
const allFiles = content.map((c) => c[1].data).filter(f => !isUnlisted(f, ctx.cfg.configuration))
const cfg = ctx.cfg.configuration
const folders: Set<SimpleSlug> = new Set(
@ -146,7 +147,7 @@ export const FolderPage: QuartzEmitterPlugin<Partial<FolderPageOptions>> = (user
yield* processFolderInfo(ctx, folderInfo, allFiles, opts, resources)
},
async *partialEmit(ctx, content, resources, changeEvents) {
const allFiles = content.map((c) => c[1].data)
const allFiles = content.map((c) => c[1].data).filter(f => !isUnlisted(f, ctx.cfg.configuration))
const cfg = ctx.cfg.configuration
// Find all folders that need to be updated based on changed files

View File

@ -12,6 +12,7 @@ import { write } from "./helpers"
import { i18n, TRANSLATIONS } from "../../i18n"
import { BuildCtx } from "../../util/ctx"
import { StaticResources } from "../../util/resources"
import { isUnlisted } from "../filters/unlisted"
interface TagPageOptions extends FullPageLayout {
sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number
@ -122,7 +123,7 @@ export const TagPage: QuartzEmitterPlugin<Partial<TagPageOptions>> = (userOpts)
]
},
async *emit(ctx, content, resources) {
const allFiles = content.map((c) => c[1].data)
const allFiles = content.map((c) => c[1].data).filter(f => !isUnlisted(f, ctx.cfg.configuration))
const cfg = ctx.cfg.configuration
const [tags, tagDescriptions] = computeTagInfo(allFiles, content, cfg.locale)
@ -131,7 +132,7 @@ export const TagPage: QuartzEmitterPlugin<Partial<TagPageOptions>> = (userOpts)
}
},
async *partialEmit(ctx, content, resources, changeEvents) {
const allFiles = content.map((c) => c[1].data)
const allFiles = content.map((c) => c[1].data).filter(f => !isUnlisted(f, ctx.cfg.configuration))
const cfg = ctx.cfg.configuration
// Find all tags that need to be updated based on changed files

View File

@ -0,0 +1,27 @@
import { minimatch } from "minimatch"
import { QuartzPluginData } from "../vfile"
import { GlobalConfiguration } from "../../cfg"
export function isUnlisted(
fileData: QuartzPluginData,
cfg: GlobalConfiguration,
unlistedPatterns?: string[]
): boolean {
const unlistedFlag: boolean =
fileData?.frontmatter?.unlisted === true ||
fileData?.frontmatter?.unlisted === "true"
if (unlistedFlag) return true
const patterns = unlistedPatterns ?? cfg.unlistedPatterns
if (patterns && patterns.length > 0 && fileData.slug) {
const slug = fileData.slug as string
for (const pattern of patterns) {
if (minimatch(slug, pattern)) {
return true
}
}
}
return false
}

View File

@ -146,6 +146,7 @@ declare module "vfile" {
socialDescription: string
publish: boolean | string
draft: boolean | string
unlisted: boolean | string
lang: string
enableToc: string
cssclasses: string[]

View File

@ -1,4 +1,5 @@
import { QuartzConfig } from "../cfg"
import { isUnlisted } from "../plugins/filters/unlisted"
import { QuartzPluginData } from "../plugins/vfile"
import { FileTrieNode } from "./fileTrie"
import { FilePath, FullSlug } from "./path"
@ -31,10 +32,16 @@ export interface BuildCtx {
incremental: boolean
}
export function trieFromAllFiles(allFiles: QuartzPluginData[]): FileTrieNode<BuildTimeTrieData> {
export function trieFromAllFiles(
allFiles: QuartzPluginData[],
cfg?: QuartzConfig
): FileTrieNode<BuildTimeTrieData> {
const trie = new FileTrieNode<BuildTimeTrieData>([])
allFiles.forEach((file) => {
if (file.frontmatter) {
if (cfg && isUnlisted(file, cfg.configuration)) {
return
}
trie.add({
...file,
slug: file.slug!,