modern toc tweaks

This commit is contained in:
Jacky Zhao 2023-06-16 19:41:59 -07:00
parent 9d2024b11c
commit 917d5791ac
17 changed files with 318 additions and 58 deletions

View file

@ -0,0 +1,53 @@
import { relativeToRoot } from "../../path"
import { QuartzEmitterPlugin } from "../types"
import path from 'path'
export const AliasRedirects: QuartzEmitterPlugin = () => ({
name: "AliasRedirects",
getQuartzComponents() {
return []
},
async emit(contentFolder, _cfg, content, _resources, emit): Promise<string[]> {
const fps: string[] = []
for (const [_tree, file] of content) {
const ogSlug = file.data.slug!
const dir = path.relative(contentFolder, file.dirname ?? contentFolder)
let aliases: string[] = []
if (file.data.frontmatter?.aliases) {
aliases = file.data.frontmatter?.aliases
} else if (file.data.frontmatter?.alias) {
aliases = [file.data.frontmatter?.alias]
}
for (const alias of aliases) {
const slug = alias.startsWith("/")
? alias
: path.posix.join(dir, alias)
const fp = slug + ".html"
const redirUrl = relativeToRoot(slug, ogSlug)
await emit({
content: `
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>${ogSlug}</title>
<link rel="canonical" href="${redirUrl}">
<meta name="robots" content="noindex">
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=${redirUrl}">
</head>
</html>
`,
slug,
ext: ".html",
})
fps.push(fp)
}
}
return fps
}
})

View file

@ -0,0 +1,25 @@
import { QuartzEmitterPlugin } from "../types"
interface Options {
domain: string
}
export const CNAME: QuartzEmitterPlugin<Options> = (opts?: Options) => ({
name: "CNAME",
getQuartzComponents() {
return []
},
async emit(_contentFolder, _cfg, _content, _resources, emit): Promise<string[]> {
const slug = "CNAME"
if (opts?.domain) {
await emit({
content: opts?.domain,
slug,
ext: "",
})
}
return ["CNAME"]
}
})

View file

@ -0,0 +1,72 @@
import { visit } from "unist-util-visit"
import { QuartzEmitterPlugin } from "../types"
import { Element } from "hast"
import path from "path"
import { trimPathSuffix } from "../../path"
interface Options {
indexAnchorLinks: boolean,
indexExternalLinks: boolean,
}
const defaultOptions: Options = {
indexAnchorLinks: false,
indexExternalLinks: false,
}
type ContentIndex = Map<string, {
title: string,
links?: string[],
tags?: string[],
content: string,
}>
export const ContentIndex: QuartzEmitterPlugin<Options> = (userOpts) => {
const opts = { ...userOpts, ...defaultOptions }
return {
name: "ContentIndex",
async emit(_contentDir, _cfg, content, _resources, emit) {
const fp = "contentIndex"
const linkIndex: ContentIndex = new Map()
for (const [tree, file] of content) {
let slug = trimPathSuffix(file.data.slug!)
const outgoing: Set<string> = new Set()
visit(tree, 'element', (node: Element) => {
if (node.tagName === 'a' && node.properties && typeof node.properties.href === 'string') {
let dest = node.properties.href
if (dest.startsWith(".")) {
const normalizedPath = path.normalize(path.join(slug, dest))
dest = trimPathSuffix(normalizedPath)
outgoing.add(dest)
} else if (dest.startsWith("#")) {
if (opts.indexAnchorLinks) {
outgoing.add(dest)
}
} else {
if (opts.indexExternalLinks) {
outgoing.add(dest)
}
}
}
})
linkIndex.set(slug, {
title: file.data.frontmatter?.title!,
links: [...outgoing],
tags: file.data.frontmatter?.tags,
content: file.data.text ?? ""
})
}
await emit({
content: JSON.stringify(Object.fromEntries(linkIndex)),
slug: fp,
ext: ".json",
})
return [`${fp}.json`]
},
getQuartzComponents: () => [],
}
}

View file

@ -1,8 +1,6 @@
import { JSResourceToScriptElement, StaticResources } from "../../resources"
import { EmitCallback, QuartzEmitterPlugin } from "../types"
import { ProcessedContent } from "../vfile"
import { QuartzEmitterPlugin } from "../types"
import { render } from "preact-render-to-string"
import { GlobalConfiguration } from "../../cfg"
import { QuartzComponent } from "../../components/types"
import { resolveToRoot } from "../../path"
import HeaderConstructor from "../../components/Header"
@ -12,7 +10,10 @@ import BodyConstructor from "../../components/Body"
interface Options {
head: QuartzComponent
header: QuartzComponent[],
body: QuartzComponent[]
body: QuartzComponent[],
left: QuartzComponent[],
right: QuartzComponent[],
footer: QuartzComponent[],
}
export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
@ -29,7 +30,7 @@ export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
getQuartzComponents() {
return [opts.head, Header, ...opts.header, ...opts.body]
},
async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
async emit(_contentDir, cfg, content, resources, emit): Promise<string[]> {
const fps: string[] = []
for (const [tree, file] of content) {
@ -53,7 +54,7 @@ export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
const doc = <html>
<Head {...componentData} />
<body>
<body data-slug={file.data.slug}>
<div id="quartz-root" class="page">
<Header {...componentData} >
{header.map(HeaderComponent => <HeaderComponent {...componentData} />)}

View file

@ -1 +1,4 @@
export { ContentPage } from './contentPage'
export { ContentIndex } from './contentIndex'
export { AliasRedirects } from './aliases'
export { CNAME } from './cname'