diff --git a/quartz/build.ts b/quartz/build.ts index 342a27c92..eff36da73 100644 --- a/quartz/build.ts +++ b/quartz/build.ts @@ -1,24 +1,24 @@ import sourceMapSupport from "source-map-support" sourceMapSupport.install(options) import path from "path" -import { PerfTimer } from "./util/perf" -import { rimraf } from "rimraf" -import { GlobbyFilterFunction, isGitIgnored } from "globby" +import {PerfTimer} from "./util/perf" +import {rimraf} from "rimraf" +import {GlobbyFilterFunction, isGitIgnored} from "globby" import chalk from "chalk" -import { parseMarkdown } from "./processors/parse" -import { filterContent } from "./processors/filter" -import { emitContent } from "./processors/emit" +import {parseMarkdown} from "./processors/parse" +import {filterContent} from "./processors/filter" +import {emitContent} from "./processors/emit" import cfg from "../quartz.config" -import { FilePath, FullSlug, joinSegments, slugifyFilePath } from "./util/path" +import {FilePath, FullSlug, joinSegments, slugifyFilePath} from "./util/path" import chokidar from "chokidar" -import { ProcessedContent } from "./plugins/vfile" -import { Argv, BuildCtx } from "./util/ctx" -import { glob, toPosixPath } from "./util/glob" -import { trace } from "./util/trace" -import { options } from "./util/sourcemap" -import { Mutex } from "async-mutex" +import {ProcessedContent} from "./plugins/vfile" +import {Argv, BuildCtx} from "./util/ctx" +import {glob, toPosixPath} from "./util/glob" +import {trace} from "./util/trace" +import {options} from "./util/sourcemap" +import {Mutex} from "async-mutex" import DepGraph from "./depgraph" -import { getStaticResourcesFromPlugins } from "./plugins" +import {getStaticResourcesFromPlugins} from "./plugins" type Dependencies = Record | null> @@ -65,17 +65,25 @@ async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) { const release = await mut.acquire() perf.addEvent("clean") - await rimraf(path.join(output, "*"), { glob: true }) - console.log(`Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`) + await rimraf(path.join(output, "*"), {glob: true}) + console.log( + `Cleaned output directory \`${output}\` in ${perf.timeSince("clean")}`, + ) perf.addEvent("glob") - const allFiles = await glob("**/*.*", argv.directory, cfg.configuration.ignorePatterns) + const allFiles = await glob( + "**/*.*", + argv.directory, + cfg.configuration.ignorePatterns, + ) const fps = allFiles.filter((fp) => fp.endsWith(".md")).sort() console.log( `Found ${fps.length} input files from \`${argv.directory}\` in ${perf.timeSince("glob")}`, ) - const filePaths = fps.map((fp) => joinSegments(argv.directory, fp) as FilePath) + const filePaths = fps.map( + (fp) => joinSegments(argv.directory, fp) as FilePath, + ) ctx.allSlugs = allFiles.map((fp) => slugifyFilePath(fp as FilePath)) const parsedFiles = await parseMarkdown(ctx, filePaths) @@ -88,12 +96,18 @@ async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) { const staticResources = getStaticResourcesFromPlugins(ctx) for (const emitter of cfg.plugins.emitters) { dependencies[emitter.name] = - (await emitter.getDependencyGraph?.(ctx, filteredContent, staticResources)) ?? null + (await emitter.getDependencyGraph?.( + ctx, + filteredContent, + staticResources, + )) ?? null } } await emitContent(ctx, filteredContent) - console.log(chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`)) + console.log( + chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`), + ) release() if (argv.serve) { @@ -109,7 +123,7 @@ async function startServing( clientRefresh: () => void, dependencies: Dependencies, // emitter name: dep graph ) { - const { argv } = ctx + const {argv} = ctx // cache file parse results const contentMap = new Map() @@ -137,11 +151,17 @@ async function startServing( ignoreInitial: true, }) - const buildFromEntry = argv.fastRebuild ? partialRebuildFromEntrypoint : rebuildFromEntrypoint + const buildFromEntry = argv.fastRebuild + ? partialRebuildFromEntrypoint + : rebuildFromEntrypoint watcher .on("add", (fp) => buildFromEntry(fp, "add", clientRefresh, buildData)) - .on("change", (fp) => buildFromEntry(fp, "change", clientRefresh, buildData)) - .on("unlink", (fp) => buildFromEntry(fp, "delete", clientRefresh, buildData)) + .on("change", (fp) => + buildFromEntry(fp, "change", clientRefresh, buildData), + ) + .on("unlink", (fp) => + buildFromEntry(fp, "delete", clientRefresh, buildData), + ) return async () => { await watcher.close() @@ -154,8 +174,8 @@ async function partialRebuildFromEntrypoint( clientRefresh: () => void, buildData: BuildData, // note: this function mutates buildData ) { - const { ctx, ignored, dependencies, contentMap, mut, toRemove } = buildData - const { argv, cfg } = ctx + const {ctx, ignored, dependencies, contentMap, mut, toRemove} = buildData + const {argv, cfg} = ctx // don't do anything for gitignored files if (ignored(filepath)) { @@ -184,12 +204,18 @@ async function partialRebuildFromEntrypoint( case "add": // add to cache when new file is added processedFiles = await parseMarkdown(ctx, [fp]) - processedFiles.forEach(([tree, vfile]) => contentMap.set(vfile.data.filePath!, [tree, vfile])) + processedFiles.forEach(([tree, vfile]) => + contentMap.set(vfile.data.filePath!, [tree, vfile]), + ) // update the dep graph by asking all emitters whether they depend on this file for (const emitter of cfg.plugins.emitters) { const emitterGraph = - (await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null + (await emitter.getDependencyGraph?.( + ctx, + processedFiles, + staticResources, + )) ?? null if (emitterGraph) { const existingGraph = dependencies[emitter.name] @@ -205,20 +231,29 @@ async function partialRebuildFromEntrypoint( case "change": // invalidate cache when file is changed processedFiles = await parseMarkdown(ctx, [fp]) - processedFiles.forEach(([tree, vfile]) => contentMap.set(vfile.data.filePath!, [tree, vfile])) + processedFiles.forEach(([tree, vfile]) => + contentMap.set(vfile.data.filePath!, [tree, vfile]), + ) // only content files can have added/removed dependencies because of transclusions if (path.extname(fp) === ".md") { for (const emitter of cfg.plugins.emitters) { // get new dependencies from all emitters for this file const emitterGraph = - (await emitter.getDependencyGraph?.(ctx, processedFiles, staticResources)) ?? null + (await emitter.getDependencyGraph?.( + ctx, + processedFiles, + staticResources, + )) ?? null // only update the graph if the emitter plugin uses the changed file // eg. Assets plugin ignores md files, so we skip updating the graph if (emitterGraph?.hasNode(fp)) { // merge the new dependencies into the dep graph - dependencies[emitter.name]?.updateIncomingEdgesForNode(emitterGraph, fp) + dependencies[emitter.name]?.updateIncomingEdgesForNode( + emitterGraph, + fp, + ) } } } @@ -281,7 +316,11 @@ async function partialRebuildFromEntrypoint( .filter((file) => !toRemove.has(file)) .map((file) => contentMap.get(file)!) - const emittedFps = await emitter.emit(ctx, upstreamContent, staticResources) + const emittedFps = await emitter.emit( + ctx, + upstreamContent, + staticResources, + ) if (ctx.argv.verbose) { for (const file of emittedFps) { @@ -293,7 +332,9 @@ async function partialRebuildFromEntrypoint( } } - console.log(`Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`) + console.log( + `Emitted ${emittedFiles} files to \`${argv.output}\` in ${perf.timeSince("rebuild")}`, + ) // CLEANUP const destinationsToDelete = new Set() @@ -328,10 +369,18 @@ async function rebuildFromEntrypoint( clientRefresh: () => void, buildData: BuildData, // note: this function mutates buildData ) { - const { ctx, ignored, mut, initialSlugs, contentMap, toRebuild, toRemove, trackedAssets } = - buildData + const { + ctx, + ignored, + mut, + initialSlugs, + contentMap, + toRebuild, + toRemove, + trackedAssets, + } = buildData - const { argv } = ctx + const {argv} = ctx // don't do anything for gitignored files if (ignored(fp)) { @@ -387,19 +436,25 @@ async function rebuildFromEntrypoint( const filteredContent = filterContent(ctx, parsedFiles) // re-update slugs - const trackedSlugs = [...new Set([...contentMap.keys(), ...toRebuild, ...trackedAssets])] + const trackedSlugs = [ + ...new Set([...contentMap.keys(), ...toRebuild, ...trackedAssets]), + ] .filter((fp) => !toRemove.has(fp)) - .map((fp) => slugifyFilePath(path.posix.relative(argv.directory, fp) as FilePath)) + .map((fp) => + slugifyFilePath(path.posix.relative(argv.directory, fp) as FilePath), + ) ctx.allSlugs = [...new Set([...initialSlugs, ...trackedSlugs])] // TODO: we can probably traverse the link graph to figure out what's safe to delete here // instead of just deleting everything - await rimraf(path.join(argv.output, ".*"), { glob: true }) + await rimraf(path.join(argv.output, ".*"), {glob: true}) await emitContent(ctx, filteredContent) console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`)) } catch (err) { - console.log(chalk.yellow(`Rebuild failed. Waiting on a change to fix the error...`)) + console.log( + chalk.yellow(`Rebuild failed. Waiting on a change to fix the error...`), + ) if (argv.verbose) { console.log(chalk.red(err)) } diff --git a/quartz/cfg.ts b/quartz/cfg.ts index 5135d5c23..0130f5d19 100644 --- a/quartz/cfg.ts +++ b/quartz/cfg.ts @@ -84,4 +84,7 @@ export interface FullPageLayout { } export type PageLayout = Pick -export type SharedLayout = Pick +export type SharedLayout = Pick< + FullPageLayout, + "head" | "header" | "footer" | "afterBody" +> diff --git a/quartz/components/ArticleTitle.tsx b/quartz/components/ArticleTitle.tsx index 318aeb24e..58438c5de 100644 --- a/quartz/components/ArticleTitle.tsx +++ b/quartz/components/ArticleTitle.tsx @@ -1,7 +1,14 @@ -import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" -import { classNames } from "../util/lang" +import { + QuartzComponent, + QuartzComponentConstructor, + QuartzComponentProps, +} from "./types" +import {classNames} from "../util/lang" -const ArticleTitle: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => { +const ArticleTitle: QuartzComponent = ({ + fileData, + displayClass, +}: QuartzComponentProps) => { const title = fileData.frontmatter?.title if (title) { return

{title}

diff --git a/quartz/components/Backlinks.tsx b/quartz/components/Backlinks.tsx index aa412a2e0..64f6d6843 100644 --- a/quartz/components/Backlinks.tsx +++ b/quartz/components/Backlinks.tsx @@ -1,8 +1,12 @@ -import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" +import { + QuartzComponent, + QuartzComponentConstructor, + QuartzComponentProps, +} from "./types" import style from "./styles/backlinks.scss" -import { resolveRelative, simplifySlug } from "../util/path" -import { i18n } from "../i18n" -import { classNames } from "../util/lang" +import {resolveRelative, simplifySlug} from "../util/path" +import {i18n} from "../i18n" +import {classNames} from "../util/lang" const Backlinks: QuartzComponent = ({ fileData, @@ -19,7 +23,9 @@ const Backlinks: QuartzComponent = ({ {backlinkFiles.length > 0 ? ( backlinkFiles.map((f) => (
  • - + {f.frontmatter?.title}
  • diff --git a/quartz/components/Body.tsx b/quartz/components/Body.tsx index 96b627883..ef40ef801 100644 --- a/quartz/components/Body.tsx +++ b/quartz/components/Body.tsx @@ -1,9 +1,13 @@ // @ts-ignore import clipboardScript from "./scripts/clipboard.inline" import clipboardStyle from "./styles/clipboard.scss" -import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" +import { + QuartzComponent, + QuartzComponentConstructor, + QuartzComponentProps, +} from "./types" -const Body: QuartzComponent = ({ children }: QuartzComponentProps) => { +const Body: QuartzComponent = ({children}: QuartzComponentProps) => { return
    {children}
    } diff --git a/quartz/components/Breadcrumbs.tsx b/quartz/components/Breadcrumbs.tsx index 9ccfb9a6a..3a4d915ba 100644 --- a/quartz/components/Breadcrumbs.tsx +++ b/quartz/components/Breadcrumbs.tsx @@ -1,8 +1,12 @@ -import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" +import { + QuartzComponent, + QuartzComponentConstructor, + QuartzComponentProps, +} from "./types" import breadcrumbsStyle from "./styles/breadcrumbs.scss" -import { FullSlug, SimpleSlug, joinSegments, resolveRelative } from "../util/path" -import { QuartzPluginData } from "../plugins/vfile" -import { classNames } from "../util/lang" +import {FullSlug, SimpleSlug, joinSegments, resolveRelative} from "../util/path" +import {QuartzPluginData} from "../plugins/vfile" +import {classNames} from "../util/lang" type CrumbData = { displayName: string @@ -40,7 +44,11 @@ const defaultOptions: BreadcrumbOptions = { showCurrentPage: true, } -function formatCrumb(displayName: string, baseSlug: FullSlug, currentSlug: SimpleSlug): CrumbData { +function formatCrumb( + displayName: string, + baseSlug: FullSlug, + currentSlug: SimpleSlug, +): CrumbData { return { displayName: displayName.replaceAll("-", " "), path: resolveRelative(baseSlug, currentSlug), @@ -49,7 +57,7 @@ function formatCrumb(displayName: string, baseSlug: FullSlug, currentSlug: Simpl export default ((opts?: Partial) => { // Merge options with defaults - const options: BreadcrumbOptions = { ...defaultOptions, ...opts } + const options: BreadcrumbOptions = {...defaultOptions, ...opts} // computed index of folder name to its associated file data let folderIndex: Map | undefined @@ -65,7 +73,11 @@ export default ((opts?: Partial) => { } // Format entry for root element - const firstEntry = formatCrumb(options.rootName, fileData.slug!, "/" as SimpleSlug) + const firstEntry = formatCrumb( + options.rootName, + fileData.slug!, + "/" as SimpleSlug, + ) const crumbs: CrumbData[] = [firstEntry] if (!folderIndex && options.resolveFrontmatterTitle) { @@ -92,7 +104,9 @@ export default ((opts?: Partial) => { let curPathSegment = slugParts[i] // Try to resolve frontmatter folder title - const currentFile = folderIndex?.get(slugParts.slice(0, i + 1).join("/")) + const currentFile = folderIndex?.get( + slugParts.slice(0, i + 1).join("/"), + ) if (currentFile) { const title = currentFile.frontmatter!.title if (title !== "index") { @@ -123,11 +137,15 @@ export default ((opts?: Partial) => { } return ( -