From 352075ae81a3304a7bfa2512ef69b1cdacb26c12 Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Mon, 12 Jun 2023 06:26:43 +0000
Subject: [PATCH] refactor plugins to be functions instead of classes

---
 quartz/plugins/types.ts                    |   41 +-
 quartz/processors/filter.ts                |    4 
 quartz/plugins/filters/draft.ts            |    9 
 quartz/plugins/transformers/latex.ts       |   20 
 quartz/plugins/transformers/description.ts |   58 +-
 package-lock.json                          |   19 
 quartz/plugins/transformers/gfm.ts         |   41 +-
 quartz/plugins/transformers/frontmatter.ts |   48 +-
 quartz/processors/parse.ts                 |    6 
 quartz/plugins/transformers/links.ts       |  112 +++---
 quartz/plugins/filters/explicit.ts         |    9 
 quartz/plugins/transformers/syntax.ts      |   15 
 quartz/components/types.ts                 |    3 
 quartz/plugins/transformers/ofm.ts         |  280 ++++++++--------
 quartz/plugins/transformers/toc.ts         |   64 +--
 package.json                               |    4 
 quartz/components/TableOfContents.tsx      |   23 
 quartz/plugins/transformers/lastmod.ts     |   85 ++--
 quartz/plugins/emitters/contentPage.tsx    |  108 +++---
 quartz.config.ts                           |   22 
 20 files changed, 464 insertions(+), 507 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 4847103..6d922f4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,6 @@
         "chalk": "^4.1.2",
         "cli-spinner": "^0.2.10",
         "esbuild-sass-plugin": "^2.9.0",
-        "flamethrower-router": "^0.0.0-meme.12",
         "github-slugger": "^2.0.0",
         "globby": "^13.1.4",
         "gray-matter": "^4.0.3",
@@ -22,9 +21,12 @@
         "hast-util-to-string": "^2.0.0",
         "is-absolute-url": "^4.0.1",
         "mdast-util-find-and-replace": "^2.2.2",
+        "mdast-util-to-string": "^3.2.0",
+        "micromorph": "^0.4.5",
         "preact": "^10.14.1",
         "preact-render-to-string": "^6.0.3",
         "pretty-time": "^1.1.0",
+        "reading-time": "^1.5.0",
         "rehype-autolink-headings": "^6.1.1",
         "rehype-katex": "^6.0.3",
         "rehype-pretty-code": "^0.9.6",
@@ -1523,11 +1525,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/flamethrower-router": {
-      "version": "0.0.0-meme.12",
-      "resolved": "https://registry.npmjs.org/flamethrower-router/-/flamethrower-router-0.0.0-meme.12.tgz",
-      "integrity": "sha512-PWcNrjzItwk61RTk/SbbKJNcAgl6qCXH8xkZjGjUGV/dgKAnURci+k+Yk8emubUQWTdAd1kSqujy0VRjoeEgxg=="
-    },
     "node_modules/foreground-child": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -3006,6 +3003,11 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/micromorph": {
+      "version": "0.4.5",
+      "resolved": "https://registry.npmjs.org/micromorph/-/micromorph-0.4.5.tgz",
+      "integrity": "sha512-Erasr0xiDvDeEhh7B/k7RFTwwfaAX10D7BMorNpokkwDh6XsRLYWDPaWF1m5JQeMSkGdqlEtQ8s68NcdDWuGgw=="
+    },
     "node_modules/mime-db": {
       "version": "1.33.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
@@ -3268,6 +3270,11 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/reading-time": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz",
+      "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg=="
+    },
     "node_modules/rehype-autolink-headings": {
       "version": "6.1.1",
       "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
diff --git a/package.json b/package.json
index fe5a517..810cf5e 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,6 @@
     "chalk": "^4.1.2",
     "cli-spinner": "^0.2.10",
     "esbuild-sass-plugin": "^2.9.0",
-    "flamethrower-router": "^0.0.0-meme.12",
     "github-slugger": "^2.0.0",
     "globby": "^13.1.4",
     "gray-matter": "^4.0.3",
@@ -38,9 +37,12 @@
     "hast-util-to-string": "^2.0.0",
     "is-absolute-url": "^4.0.1",
     "mdast-util-find-and-replace": "^2.2.2",
+    "mdast-util-to-string": "^3.2.0",
+    "micromorph": "^0.4.5",
     "preact": "^10.14.1",
     "preact-render-to-string": "^6.0.3",
     "pretty-time": "^1.1.0",
+    "reading-time": "^1.5.0",
     "rehype-autolink-headings": "^6.1.1",
     "rehype-katex": "^6.0.3",
     "rehype-pretty-code": "^0.9.6",
diff --git a/quartz.config.ts b/quartz.config.ts
index 3a1d433..ab7e990 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -39,23 +39,23 @@
   },
   plugins: {
     transformers: [
-      new Plugin.FrontMatter(),
-      new Plugin.Description(),
-      new Plugin.TableOfContents({ showByDefault: true }),
-      new Plugin.CreatedModifiedDate({
+      Plugin.FrontMatter(),
+      Plugin.Description(),
+      Plugin.TableOfContents({ showByDefault: true }),
+      Plugin.CreatedModifiedDate({
         priority: ['frontmatter', 'filesystem'] // you can add 'git' here for last modified from Git but this makes the build slower
       }),
-      new Plugin.GitHubFlavoredMarkdown(),
-      new Plugin.ObsidianFlavoredMarkdown(),
-      new Plugin.ResolveLinks(),
-      new Plugin.SyntaxHighlighting(),
-      new Plugin.Katex(),
+      Plugin.GitHubFlavoredMarkdown(),
+      Plugin.ObsidianFlavoredMarkdown(),
+      Plugin.ResolveLinks(),
+      Plugin.SyntaxHighlighting(),
+      Plugin.Katex(),
     ],
     filters: [
-      new Plugin.RemoveDrafts()
+      Plugin.RemoveDrafts()
     ],
     emitters: [
-      new Plugin.ContentPage({
+      Plugin.ContentPage({
         head: Component.Head,
         header: [Component.PageTitle, Component.Spacer, Component.Darkmode],
         body: [Component.ArticleTitle, Component.ReadingTime, Component.TableOfContents, Component.Content]
diff --git a/quartz/components/TableOfContents.tsx b/quartz/components/TableOfContents.tsx
index 8192da4..1f331ed 100644
--- a/quartz/components/TableOfContents.tsx
+++ b/quartz/components/TableOfContents.tsx
@@ -1,24 +1,19 @@
 import { QuartzComponentProps } from "./types"
 import style from "./styles/toc.scss"
 
-export default function TableOfContents({ fileData, position }: QuartzComponentProps) {
+export default function TableOfContents({ fileData }: QuartzComponentProps) {
   if (!fileData.toc) {
     return null
   }
 
-  if (position === 'body') {
-    // TODO: animate this
-    return <details className="toc" open>
-      <summary><h3>Table of Contents</h3></summary>
-      <ul>
-        {fileData.toc.map(tocEntry => <li key={tocEntry.slug} className={`depth-${tocEntry.depth}`}>
-          <a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
-        </li>)}
-      </ul>
-    </details>
-  } else if (position === 'sidebar') {
-    // TODO
-  }
+  return <details class="toc" open>
+    <summary><h3>Table of Contents</h3></summary>
+    <ul>
+      {fileData.toc.map(tocEntry => <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
+        <a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
+      </li>)}
+    </ul>
+  </details>
 }
 
 TableOfContents.css = style
diff --git a/quartz/components/types.ts b/quartz/components/types.ts
index 93f6a4b..cb84edc 100644
--- a/quartz/components/types.ts
+++ b/quartz/components/types.ts
@@ -10,7 +10,6 @@
   cfg: GlobalConfiguration
   children: QuartzComponent[] | JSX.Element[]
   tree: Node<QuartzPluginData>
-  position?: 'sidebar' | 'header' | 'body'
 }
 
 export type QuartzComponent = ComponentType<QuartzComponentProps> & {
@@ -18,3 +17,5 @@
   beforeDOMLoaded?: string,
   afterDOMLoaded?: string,
 }
+
+export type QuartzComponentConstructor<Options extends object> = (opts: Options) => QuartzComponent
diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx
index d44b709..b7059f8 100644
--- a/quartz/plugins/emitters/contentPage.tsx
+++ b/quartz/plugins/emitters/contentPage.tsx
@@ -15,66 +15,64 @@
   body: QuartzComponent[]
 }
 
-export class ContentPage extends QuartzEmitterPlugin {
-  name = "ContentPage"
-  opts: Options
-
-  constructor(opts: Options) {
-    super()
-    this.opts = opts
+export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
+  if (!opts) {
+    throw new Error("ContentPage must be initialized with options specifiying the components to use")
   }
 
-  getQuartzComponents(): QuartzComponent[] {
-    return [this.opts.head, Header, ...this.opts.header, ...this.opts.body]
-  }
+  return {
+    name: "ContentPage",
+    getQuartzComponents() {
+      return [opts.head, Header, ...opts.header, ...opts.body]
+    },
+    async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
+      const fps: string[] = []
 
-  async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
-    const fps: string[] = []
+      const { head: Head, header, body } = opts
+      for (const [tree, file] of content) {
+        const baseDir = resolveToRoot(file.data.slug!)
+        const pageResources: StaticResources = {
+          css: [baseDir + "/index.css", ...resources.css],
+          js: [
+            { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" },
+            ...resources.js,
+            { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", type: 'module' }
+          ]
+        }
 
-    const { head: Head, header, body } = this.opts
-    for (const [tree, file] of content) {
-      const baseDir = resolveToRoot(file.data.slug!)
-      const pageResources: StaticResources = {
-        css: [baseDir + "/index.css", ...resources.css],
-        js: [
-          { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" },
-          ...resources.js,
-          { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", type: 'module' }
-        ]
+        const componentData: QuartzComponentProps = {
+          fileData: file.data,
+          externalResources: pageResources,
+          cfg,
+          children: [],
+          tree
+        }
+
+        const doc = <html>
+          <Head {...componentData} />
+          <body>
+            <div id="quartz-root" class="page">
+              <Header {...componentData} >
+                {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
+              </Header>
+              <Body {...componentData}>
+                {body.map(BodyComponent => <BodyComponent {...componentData} />)}
+              </Body>
+            </div>
+          </body>
+          {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} {...resource} />)}
+        </html>
+
+        const fp = file.data.slug + ".html"
+        await emit({
+          content: "<!DOCTYPE html>\n" + render(doc),
+          slug: file.data.slug!,
+          ext: ".html",
+        })
+
+        fps.push(fp)
       }
-
-      const componentData: QuartzComponentProps = {
-        fileData: file.data,
-        externalResources: pageResources,
-        cfg,
-        children: [],
-        tree
-      }
-
-      const doc = <html>
-        <Head {...componentData} />
-        <body>
-          <div id="quartz-root" class="page">
-            <Header {...componentData} >
-              {header.map(HeaderComponent => <HeaderComponent {...componentData} position="header" />)}
-            </Header>
-            <Body {...componentData}>
-              {body.map(BodyComponent => <BodyComponent {...componentData } position="body" />)}
-            </Body>
-          </div>
-        </body>
-        {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} {...resource} />)}
-      </html>
-
-      const fp = file.data.slug + ".html"
-      await emit({
-        content: "<!DOCTYPE html>\n" + render(doc),
-        slug: file.data.slug!,
-        ext: ".html",
-      })
-
-      fps.push(fp)
+      return fps
     }
-    return fps
   }
 }
diff --git a/quartz/plugins/filters/draft.ts b/quartz/plugins/filters/draft.ts
index 06083d2..630033d 100644
--- a/quartz/plugins/filters/draft.ts
+++ b/quartz/plugins/filters/draft.ts
@@ -1,10 +1,9 @@
 import { QuartzFilterPlugin } from "../types"
-import { ProcessedContent } from "../vfile"
 
-export class RemoveDrafts extends QuartzFilterPlugin {
-  name = "RemoveDrafts"
-  shouldPublish([_tree, vfile]: ProcessedContent): boolean {
+export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
+  name: "RemoveDrafts",
+  shouldPublish([_tree, vfile]) {
     const draftFlag: boolean = vfile.data?.frontmatter?.draft ?? false
     return !draftFlag
   }
-}
+})
diff --git a/quartz/plugins/filters/explicit.ts b/quartz/plugins/filters/explicit.ts
index 7dad8cb..da5125b 100644
--- a/quartz/plugins/filters/explicit.ts
+++ b/quartz/plugins/filters/explicit.ts
@@ -1,10 +1,9 @@
 import { QuartzFilterPlugin } from "../types"
-import { ProcessedContent } from "../vfile"
 
-export class ExplicitPublish extends QuartzFilterPlugin {
-  name = "ExplicitPublish"
-  shouldPublish([_tree, vfile]: ProcessedContent): boolean {
+export const ExplicitPublish: QuartzFilterPlugin = () => ({
+  name: "ExplicitPublish",
+  shouldPublish([_tree, vfile]) {
     const publishFlag: boolean = vfile.data?.frontmatter?.publish ?? false
     return publishFlag
   }
-}
+})
diff --git a/quartz/plugins/transformers/description.ts b/quartz/plugins/transformers/description.ts
index b24dd1c..ed59f82 100644
--- a/quartz/plugins/transformers/description.ts
+++ b/quartz/plugins/transformers/description.ts
@@ -1,4 +1,3 @@
-import { PluggableList } from "unified"
 import { Root as HTMLRoot } from 'hast'
 import { toString } from "hast-util-to-string"
 import { QuartzTransformerPlugin } from "../types"
@@ -11,41 +10,36 @@
   descriptionLength: 150
 }
 
-export class Description extends QuartzTransformerPlugin {
-  name = "Description"
-  opts: Options
+export const Description: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
+  const opts = { ...defaultOptions, ...userOpts }
+  return {
+    name: "Description",
+    markdownPlugins() {
+      return []
+    },
+    htmlPlugins() {
+      return [
+        () => {
+          return async (tree: HTMLRoot, file) => {
+            const frontMatterDescription = file.data.frontmatter?.description
+            const text = toString(tree)
 
-  constructor(opts?: Partial<Options>) {
-    super()
-    this.opts = { ...defaultOptions, ...opts }
-  }
+            const desc = frontMatterDescription ?? text
+            const sentences = desc.replace(/\s+/g, ' ').split('.')
+            let finalDesc = ""
+            let sentenceIdx = 0
+            const len = opts.descriptionLength
+            while (finalDesc.length < len) {
+              finalDesc += sentences[sentenceIdx] + '.'
+              sentenceIdx++
+            }
 
-  markdownPlugins(): PluggableList {
-    return []
-  }
-
-  htmlPlugins(): PluggableList {
-    return [
-      () => {
-        return async (tree: HTMLRoot, file) => {
-          const frontMatterDescription = file.data.frontmatter?.description
-          const text = toString(tree)
-
-          const desc = frontMatterDescription ?? text
-          const sentences = desc.replace(/\s+/g, ' ').split('.')
-          let finalDesc = ""
-          let sentenceIdx = 0
-          const len = this.opts.descriptionLength
-          while (finalDesc.length < len) {
-            finalDesc += sentences[sentenceIdx] + '.'
-            sentenceIdx++
+            file.data.description = finalDesc
+            file.data.text = text
           }
-
-          file.data.description = finalDesc
-          file.data.text = text
         }
-      }
-    ]
+      ]
+    }
   }
 }
 
diff --git a/quartz/plugins/transformers/frontmatter.ts b/quartz/plugins/transformers/frontmatter.ts
index 0baec9e..5568463 100644
--- a/quartz/plugins/transformers/frontmatter.ts
+++ b/quartz/plugins/transformers/frontmatter.ts
@@ -1,4 +1,3 @@
-import { PluggableList } from "unified"
 import matter from "gray-matter"
 import remarkFrontmatter from 'remark-frontmatter'
 import { QuartzTransformerPlugin } from "../types"
@@ -13,35 +12,30 @@
   delims: '---'
 }
 
-export class FrontMatter extends QuartzTransformerPlugin {
-  name = "FrontMatter"
-  opts: Options
+export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
+  const opts = { ...defaultOptions, ...userOpts }
+  return {
+    name: "FrontMatter",
+    markdownPlugins() {
+      return [
+        remarkFrontmatter,
+        () => {
+          return (_, file) => {
+            const { data } = matter(file.value, opts)
 
-  constructor(opts?: Partial<Options>) {
-    super()
-    this.opts = { ...defaultOptions, ...opts }
-  }
-
-  markdownPlugins(): PluggableList {
-    return [
-      remarkFrontmatter,
-      () => {
-        return (_, file) => {
-          const { data } = matter(file.value, this.opts)
-
-          // fill in frontmatter
-          file.data.frontmatter = {
-            title: file.stem ?? "Untitled",
-            tags: [],
-            ...data
+            // fill in frontmatter
+            file.data.frontmatter = {
+              title: file.stem ?? "Untitled",
+              tags: [],
+              ...data
+            }
           }
         }
-      }
-    ]
-  }
-
-  htmlPlugins(): PluggableList {
-    return []
+      ]
+    },
+    htmlPlugins() {
+      return []
+    }
   }
 }
 
diff --git a/quartz/plugins/transformers/gfm.ts b/quartz/plugins/transformers/gfm.ts
index 72f9870..54f8ca6 100644
--- a/quartz/plugins/transformers/gfm.ts
+++ b/quartz/plugins/transformers/gfm.ts
@@ -15,27 +15,24 @@
   linkHeadings: true
 }
 
-export class GitHubFlavoredMarkdown extends QuartzTransformerPlugin {
-  name = "GitHubFlavoredMarkdown"
-  opts: Options
-
-  constructor(opts?: Partial<Options>) {
-    super()
-    this.opts = { ...defaultOptions, ...opts }
-  }
-
-  markdownPlugins(): PluggableList {
-    return this.opts.enableSmartyPants ? [remarkGfm] : [remarkGfm, smartypants]
-  }
-
-  htmlPlugins(): PluggableList {
-    return this.opts.linkHeadings
-      ? [rehypeSlug, [rehypeAutolinkHeadings, {
-        behavior: 'append', content: {
-          type: 'text',
-          value: ' §'
-        }
-      }]]
-      : []
+export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
+  const opts = { ...defaultOptions, ...userOpts }
+  return {
+    name: "GitHubFlavoredMarkdown",
+    markdownPlugins() {
+      return opts.enableSmartyPants ? [remarkGfm] : [remarkGfm, smartypants]
+    },
+    htmlPlugins() {
+      if (opts.linkHeadings) {
+        return [rehypeSlug, [rehypeAutolinkHeadings, {
+          behavior: 'append', content: {
+            type: 'text',
+            value: ' §'
+          }
+        }]]
+      } else {
+        return []
+      }
+    }
   }
 }
diff --git a/quartz/plugins/transformers/lastmod.ts b/quartz/plugins/transformers/lastmod.ts
index ef33afe..b7514e4 100644
--- a/quartz/plugins/transformers/lastmod.ts
+++ b/quartz/plugins/transformers/lastmod.ts
@@ -1,4 +1,3 @@
-import { PluggableList } from "unified"
 import fs from "fs"
 import path from 'path'
 import { Repository } from "@napi-rs/simple-git"
@@ -12,59 +11,51 @@
   priority: ['frontmatter', 'git', 'filesystem']
 }
 
-export class CreatedModifiedDate extends QuartzTransformerPlugin {
-  name = "CreatedModifiedDate"
-  opts: Options
+export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
+  const opts = { ...defaultOptions, ...userOpts }
+  return {
+    name: "CreatedModifiedDate",
+    markdownPlugins() {
+      return [
+        () => {
+          let repo: Repository | undefined = undefined
+          return async (_tree, file) => {
+            let created: undefined | Date = undefined
+            let modified: undefined | Date = undefined
+            let published: undefined | Date = undefined
 
-  constructor(opts?: Partial<Options>) {
-    super()
-    this.opts = {
-      ...defaultOptions,
-      ...opts,
-    }
-  }
+            const fp = path.join(file.cwd, file.data.filePath as string)
+            for (const source of opts.priority) {
+              if (source === "filesystem") {
+                const st = await fs.promises.stat(fp)
+                created ||= new Date(st.birthtimeMs)
+                modified ||= new Date(st.mtimeMs)
+              } else if (source === "frontmatter" && file.data.frontmatter) {
+                created ||= file.data.frontmatter.date
+                modified ||= file.data.frontmatter.lastmod
+                modified ||= file.data.frontmatter["last-modified"]
+                published ||= file.data.frontmatter.publishDate
+              } else if (source === "git") {
+                if (!repo) {
+                  repo = new Repository(file.cwd)
+                }
 
-  markdownPlugins(): PluggableList {
-    return [
-      () => {
-        let repo: Repository | undefined = undefined
-        return async (_tree, file) => {
-          let created: undefined | Date = undefined
-          let modified: undefined | Date = undefined
-          let published: undefined | Date = undefined
-
-          const fp = path.join(file.cwd, file.data.filePath as string)
-          for (const source of this.opts.priority) {
-            if (source === "filesystem") {
-              const st = await fs.promises.stat(fp)
-              created ||= new Date(st.birthtimeMs)
-              modified ||= new Date(st.mtimeMs)
-            } else if (source === "frontmatter" && file.data.frontmatter) {
-              created ||= file.data.frontmatter.date
-              modified ||= file.data.frontmatter.lastmod
-              modified ||= file.data.frontmatter["last-modified"]
-              published ||= file.data.frontmatter.publishDate
-            } else if (source === "git") {
-              if (!repo) {
-                repo = new Repository(file.cwd)
+                modified ||= new Date(await repo.getFileLatestModifiedDateAsync(file.data.filePath!))
               }
+            }
 
-              modified ||= new Date(await repo.getFileLatestModifiedDateAsync(file.data.filePath!))
+            file.data.dates = {
+              created: created ?? new Date(),
+              modified: modified ?? new Date(),
+              published: published ?? new Date()
             }
           }
-
-          file.data.dates = {
-            created: created ?? new Date(),
-            modified: modified ?? new Date(),
-            published: published ?? new Date()
-          }
         }
-      }
-    ]
-  }
-
-  htmlPlugins(): PluggableList {
-    return []
+      ]
+    },
+    htmlPlugins() {
+      return []
+    }
   }
 }
 
diff --git a/quartz/plugins/transformers/latex.ts b/quartz/plugins/transformers/latex.ts
index f4155f6..86af69f 100644
--- a/quartz/plugins/transformers/latex.ts
+++ b/quartz/plugins/transformers/latex.ts
@@ -1,24 +1,20 @@
-import { PluggableList } from "unified"
 import remarkMath from "remark-math"
 import rehypeKatex from 'rehype-katex'
-import { StaticResources } from "../../resources"
 import { QuartzTransformerPlugin } from "../types"
 
-export class Katex extends QuartzTransformerPlugin {
-  name = "Katex"
-  markdownPlugins(): PluggableList {
+export const Katex: QuartzTransformerPlugin = () => ({
+  name: "Katex",
+  markdownPlugins() {
     return [remarkMath]
-  }
-
-  htmlPlugins(): PluggableList {
+  },
+  htmlPlugins() {
     return [
       [rehypeKatex, {
         output: 'html',
       }]
     ]
-  }
-
-  externalResources: Partial<StaticResources> = {
+  },
+  externalResources: {
     css: [
       // base css
       "https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css",
@@ -31,4 +27,4 @@
       }
     ]
   }
-}
+})
diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts
index 4bcbe82..1619344 100644
--- a/quartz/plugins/transformers/links.ts
+++ b/quartz/plugins/transformers/links.ts
@@ -1,4 +1,3 @@
-import { PluggableList } from "unified"
 import { QuartzTransformerPlugin } from "../types"
 import { relative, relativeToRoot, slugify } from "../../path"
 import path from "path"
@@ -17,65 +16,60 @@
   prettyLinks: true
 }
 
-export class ResolveLinks extends QuartzTransformerPlugin {
-  name = "LinkProcessing"
-  opts: Options
-
-  constructor(opts?: Partial<Options>) {
-    super()
-    this.opts = { ...defaultOptions, ...opts }
-  }
-
-  markdownPlugins(): PluggableList {
-    return []
-  }
-
-  htmlPlugins(): PluggableList {
-    return [() => {
-      return (tree, file) => {
-        const curSlug = file.data.slug!
-        const transformLink = (target: string) => {
-          const targetSlug = slugify(decodeURI(target).trim())
-          if (this.opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
-            return './' + relative(curSlug, targetSlug)
-          } else {
-            return './' + relativeToRoot(curSlug, targetSlug)
+export const ResolveLinks: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
+  const opts = { ...defaultOptions, ...userOpts }
+  return {
+    name: "LinkProcessing",
+    markdownPlugins() {
+      return []
+    },
+    htmlPlugins() {
+      return [() => {
+        return (tree, file) => {
+          const curSlug = file.data.slug!
+          const transformLink = (target: string) => {
+            const targetSlug = slugify(decodeURI(target).trim())
+            if (opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
+              return './' + relative(curSlug, targetSlug)
+            } else {
+              return './' + relativeToRoot(curSlug, targetSlug)
+            }
           }
+
+          visit(tree, 'element', (node, _index, _parent) => {
+            // rewrite all links
+            if (
+              node.tagName === 'a' &&
+              node.properties &&
+              typeof node.properties.href === 'string'
+            ) {
+              node.properties.className = isAbsoluteUrl(node.properties.href) ? "external" : "internal"
+
+              // don't process external links or intra-document anchors
+              if (!(isAbsoluteUrl(node.properties.href) || node.properties.href.startsWith("#"))) {
+                node.properties.href = transformLink(node.properties.href)
+              }
+
+              // rewrite link internals if prettylinks is on
+              if (opts.prettyLinks && node.children.length === 1 && node.children[0].type === 'text') {
+                node.children[0].value = path.basename(node.children[0].value)
+              }
+            }
+
+            // transform all images
+            if (
+              node.tagName === 'img' &&
+              node.properties &&
+              typeof node.properties.src === 'string'
+            ) {
+              if (!isAbsoluteUrl(node.properties.src)) {
+                const ext = path.extname(node.properties.src)
+                node.properties.src = transformLink(path.join("assets", node.properties.src)) + ext
+              }
+            }
+          })
         }
-
-        visit(tree, 'element', (node, _index, _parent) => {
-          // rewrite all links
-          if (
-            node.tagName === 'a' &&
-            node.properties &&
-            typeof node.properties.href === 'string'
-          ) {
-            node.properties.className = isAbsoluteUrl(node.properties.href) ? "external" : "internal"
-
-            // don't process external links or intra-document anchors
-            if (!(isAbsoluteUrl(node.properties.href) || node.properties.href.startsWith("#"))) {
-              node.properties.href = transformLink(node.properties.href)
-            }
-
-            // rewrite link internals if prettylinks is on
-            if (this.opts.prettyLinks && node.children.length === 1 && node.children[0].type === 'text') {
-              node.children[0].value = path.basename(node.children[0].value)
-            }
-          }
-
-          // transform all images
-          if (
-            node.tagName === 'img' &&
-            node.properties &&
-            typeof node.properties.src === 'string'
-          ) {
-            if (!isAbsoluteUrl(node.properties.src)) {
-              const ext = path.extname(node.properties.src)
-              node.properties.src = transformLink(path.join("assets", node.properties.src)) + ext
-            }
-          }
-        })
-      }
-    }]
+      }]
+    }
   }
 }
diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts
index 23ed37c..1733b94 100644
--- a/quartz/plugins/transformers/ofm.ts
+++ b/quartz/plugins/transformers/ofm.ts
@@ -89,174 +89,168 @@
   return s.substring(0, 1).toUpperCase() + s.substring(1);
 }
 
-export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
-  name = "ObsidianFlavoredMarkdown"
-  opts: Options
+export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
+  const opts = { ...defaultOptions, ...userOpts }
+  return {
+    name: "ObsidianFlavoredMarkdown",
+    markdownPlugins() {
+      const plugins: PluggableList = []
+      if (opts.wikilinks) {
+        plugins.push(() => {
+          // Match wikilinks 
+          // !?               -> optional embedding
+          // \[\[             -> open brace
+          // ([^\[\]\|\#]+)   -> one or more non-special characters ([,],|, or #) (name)
+          // (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
+          // (|[^\[\]\|\#]+)? -> | then one or more non-special characters (alias)
+          const backlinkRegex = new RegExp(/!?\[\[([^\[\]\|\#]+)(#[^\[\]\|\#]+)?(\|[^\[\]\|\#]+)?\]\]/, "g")
+          return (tree: Root, _file) => {
+            findAndReplace(tree, backlinkRegex, (value: string, ...capture: string[]) => {
+              const [fp, rawHeader, rawAlias] = capture
+              const anchor = rawHeader?.trim() ?? ""
+              const alias = rawAlias?.slice(1).trim()
 
-  constructor(opts?: Partial<Options>) {
-    super()
-    this.opts = { ...defaultOptions, ...opts }
-  }
-
-  markdownPlugins(): PluggableList {
-    const plugins: PluggableList = []
-
-    if (this.opts.wikilinks) {
-      plugins.push(() => {
-        // Match wikilinks 
-        // !?               -> optional embedding
-        // \[\[             -> open brace
-        // ([^\[\]\|\#]+)   -> one or more non-special characters ([,],|, or #) (name)
-        // (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
-        // (|[^\[\]\|\#]+)? -> | then one or more non-special characters (alias)
-        const backlinkRegex = new RegExp(/!?\[\[([^\[\]\|\#]+)(#[^\[\]\|\#]+)?(\|[^\[\]\|\#]+)?\]\]/, "g")
-        return (tree: Root, _file) => {
-          findAndReplace(tree, backlinkRegex, (value: string, ...capture: string[]) => {
-            const [fp, rawHeader, rawAlias] = capture
-            const anchor = rawHeader?.trim() ?? ""
-            const alias = rawAlias?.slice(1).trim()
-
-            // embed cases
-            if (value.startsWith("!")) {
-              const ext = path.extname(fp).toLowerCase()
-              const url = slugify(fp.trim()) + ext
-              if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg"].includes(ext)) {
-                const dims = alias ?? ""
-                let [width, height] = dims.split("x", 2)
-                width ||= "auto"
-                height ||= "auto"
-                return {
-                  type: 'image',
-                  url,
-                  data: {
-                    hProperties: {
-                      width, height
+              // embed cases
+              if (value.startsWith("!")) {
+                const ext = path.extname(fp).toLowerCase()
+                const url = slugify(fp.trim()) + ext
+                if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg"].includes(ext)) {
+                  const dims = alias ?? ""
+                  let [width, height] = dims.split("x", 2)
+                  width ||= "auto"
+                  height ||= "auto"
+                  return {
+                    type: 'image',
+                    url,
+                    data: {
+                      hProperties: {
+                        width, height
+                      }
                     }
                   }
+                } else if ([".mp4", ".webm", ".ogv", ".mov", ".mkv"].includes(ext)) {
+                  return {
+                    type: 'html',
+                    value: `<video src="${url}" controls></video>`
+                  }
+                } else if ([".mp3", ".webm", ".wav", ".m4a", ".ogg", ".3gp", ".flac"].includes(ext)) {
+                  return {
+                    type: 'html',
+                    value: `<audio src="${url}" controls></audio>`
+                  }
+                } else if ([".pdf"].includes(ext)) {
+                  return {
+                    type: 'html',
+                    value: `<iframe src="${url}"></iframe>`
+                  }
                 }
-              } else if ([".mp4", ".webm", ".ogv", ".mov", ".mkv"].includes(ext)) {
-                return {
-                  type: 'html',
-                  value: `<video src="${url}" controls></video>`
-                }
-              } else if ([".mp3", ".webm", ".wav", ".m4a", ".ogg", ".3gp", ".flac"].includes(ext)) {
-                return {
-                  type: 'html',
-                  value: `<audio src="${url}" controls></audio>`
-                }
-              } else if ([".pdf"].includes(ext)) {
-                return {
-                  type: 'html',
-                  value: `<iframe src="${url}"></iframe>`
-                }
+                // otherwise, fall through to regular link
               }
-              // otherwise, fall through to regular link
-            }
 
-            // internal link
-            const url = slugify(fp.trim() + anchor)
-            return {
-              type: 'link',
-              url,
-              children: [{
-                type: 'text',
-                value: alias ?? fp
-              }]
-            }
-          })
+              // internal link
+              const url = slugify(fp.trim() + anchor)
+              return {
+                type: 'link',
+                url,
+                children: [{
+                  type: 'text',
+                  value: alias ?? fp
+                }]
+              }
+            })
+          }
         }
+        )
       }
-      )
-    }
 
-    if (this.opts.highlight) {
-      plugins.push(() => {
-        // Match highlights 
-        const highlightRegex = new RegExp(/==(.+)==/, "g")
-        return (tree: Root, _file) => {
-          findAndReplace(tree, highlightRegex, (_value: string, ...capture: string[]) => {
-            const [inner] = capture
-            return {
-              type: 'html',
-              value: `<span class="text-highlight">${inner}</span>`
-            }
-          })
-        }
-      })
-    }
+      if (opts.highlight) {
+        plugins.push(() => {
+          // Match highlights 
+          const highlightRegex = new RegExp(/==(.+)==/, "g")
+          return (tree: Root, _file) => {
+            findAndReplace(tree, highlightRegex, (_value: string, ...capture: string[]) => {
+              const [inner] = capture
+              return {
+                type: 'html',
+                value: `<span class="text-highlight">${inner}</span>`
+              }
+            })
+          }
+        })
+      }
 
-    if (this.opts.callouts) {
-      plugins.push(() => {
-        // from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts
-        const calloutRegex = new RegExp(/^\[\!(\w+)\]([+-]?)/)
-        return (tree: Root, _file) => {
-          visit(tree, "blockquote", (node) => {
-            if (node.children.length === 0) {
-              return
-            }
+      if (opts.callouts) {
+        plugins.push(() => {
+          // from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts
+          const calloutRegex = new RegExp(/^\[\!(\w+)\]([+-]?)/)
+          return (tree: Root, _file) => {
+            visit(tree, "blockquote", (node) => {
+              if (node.children.length === 0) {
+                return
+              }
 
-            // find first line
-            const firstChild = node.children[0]
-            if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
-              return
-            }
+              // find first line
+              const firstChild = node.children[0]
+              if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
+                return
+              }
 
-            const text = firstChild.children[0].value
-            const [firstLine, ...remainingLines] = text.split("\n")
-            const remainingText = remainingLines.join("\n")
+              const text = firstChild.children[0].value
+              const [firstLine, ...remainingLines] = text.split("\n")
+              const remainingText = remainingLines.join("\n")
 
-            const match = firstLine.match(calloutRegex)
-            if (match && match.input) {
-              const [calloutDirective, typeString, collapseChar] = match
-              const calloutType = typeString.toLowerCase() as keyof typeof callouts
-              const collapse = collapseChar === "+" || collapseChar === "-"
-              const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
-              const title = match.input.slice(calloutDirective.length).trim() || capitalize(calloutType)
+              const match = firstLine.match(calloutRegex)
+              if (match && match.input) {
+                const [calloutDirective, typeString, collapseChar] = match
+                const calloutType = typeString.toLowerCase() as keyof typeof callouts
+                const collapse = collapseChar === "+" || collapseChar === "-"
+                const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
+                const title = match.input.slice(calloutDirective.length).trim() || capitalize(calloutType)
 
-              const titleNode: HTML = {
-                type: "html",
-                value: `<div 
+                const titleNode: HTML = {
+                  type: "html",
+                  value: `<div 
                   class="callout-title"
                 >
                   <div class="callout-icon">${callouts[canonicalizeCallout(calloutType)]}</div>
                   <div class="callout-title-inner">${title}</div>
                 </div>`
-              }
+                }
 
-              const blockquoteContent: (BlockContent | DefinitionContent)[] = [titleNode]
-              if (remainingText.length > 0) {
-                blockquoteContent.push({
-                  type: 'paragraph',
-                  children: [{
-                    type: 'text',
-                    value: remainingText,
-                  }]
+                const blockquoteContent: (BlockContent | DefinitionContent)[] = [titleNode]
+                if (remainingText.length > 0) {
+                  blockquoteContent.push({
+                    type: 'paragraph',
+                    children: [{
+                      type: 'text',
+                      value: remainingText,
+                    }]
 
-                })
-              }
+                  })
+                }
 
-              // replace first line of blockquote with title and rest of the paragraph text
-              node.children.splice(0, 1, ...blockquoteContent)
+                // replace first line of blockquote with title and rest of the paragraph text
+                node.children.splice(0, 1, ...blockquoteContent)
 
-              // add properties to base blockquote
-              node.data = {
-                hProperties: {
-                  ...(node.data?.hProperties ?? {}),
-                  className: `callout ${collapse ? "is-collapsible" : ""} ${defaultState === "collapsed" ? "is-collapsed" : ""}`,
-                  "data-callout": calloutType,
-                  "data-callout-fold": collapse,
+                // add properties to base blockquote
+                node.data = {
+                  hProperties: {
+                    ...(node.data?.hProperties ?? {}),
+                    className: `callout ${collapse ? "is-collapsible" : ""} ${defaultState === "collapsed" ? "is-collapsed" : ""}`,
+                    "data-callout": calloutType,
+                    "data-callout-fold": collapse,
+                  }
                 }
               }
-            }
-          })
-        }
-      })
+            })
+          }
+        })
+      }
+      return plugins
+    },
+
+    htmlPlugins() {
+      return [rehypeRaw]
     }
-
-    return plugins
-  }
-
-  htmlPlugins(): PluggableList {
-    return [rehypeRaw]
   }
 }
diff --git a/quartz/plugins/transformers/syntax.ts b/quartz/plugins/transformers/syntax.ts
index f09daaa..16424ec 100644
--- a/quartz/plugins/transformers/syntax.ts
+++ b/quartz/plugins/transformers/syntax.ts
@@ -1,15 +1,12 @@
-import { PluggableList } from "unified"
 import { QuartzTransformerPlugin } from "../types"
 import rehypePrettyCode, { Options as CodeOptions } from "rehype-pretty-code"
 
-export class SyntaxHighlighting extends QuartzTransformerPlugin {
-  name = "SyntaxHighlighting"
-
-  markdownPlugins(): PluggableList {
+export const SyntaxHighlighting: QuartzTransformerPlugin = () => ({
+  name: "SyntaxHighlighting",
+  markdownPlugins() {
     return []
-  }
-
-  htmlPlugins(): PluggableList {
+  },
+  htmlPlugins() {
     return [[rehypePrettyCode, {
       theme: 'css-variables',
       onVisitLine(node) {
@@ -25,4 +22,4 @@
       },
     } satisfies Partial<CodeOptions>]]
   }
-}
+})
diff --git a/quartz/plugins/transformers/toc.ts b/quartz/plugins/transformers/toc.ts
index 863e3a1..2141a87 100644
--- a/quartz/plugins/transformers/toc.ts
+++ b/quartz/plugins/transformers/toc.ts
@@ -1,4 +1,3 @@
-import { PluggableList } from "unified"
 import { QuartzTransformerPlugin } from "../types"
 import { Root } from "mdast"
 import { visit } from "unist-util-visit"
@@ -23,44 +22,39 @@
   slug: string
 }
 
-export class TableOfContents extends QuartzTransformerPlugin {
-  name = "TableOfContents"
-  opts: Options
+export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
+  const opts = { ...defaultOptions, ...userOpts }
+  return {
+    name: "TableOfContents",
+    markdownPlugins() {
+      return [() => {
+        return async (tree: Root, file) => {
+          const display = file.data.frontmatter?.enableToc ?? opts.showByDefault
+          if (display) {
+            const toc: TocEntry[] = []
+            let highestDepth: number = opts.maxDepth
+            visit(tree, 'heading', (node) => {
+              if (node.depth <= opts.maxDepth) {
+                const text = toString(node)
+                highestDepth = Math.min(highestDepth, node.depth)
+                toc.push({
+                  depth: node.depth,
+                  text,
+                  slug: slugAnchor.slug(text)
+                })
+              }
+            })
 
-  constructor(opts?: Partial<Options>) {
-    super()
-    this.opts = { ...defaultOptions, ...opts }
-  }
-
-  markdownPlugins(): PluggableList {
-    return [() => {
-      return async (tree: Root, file) => {
-        const display = file.data.frontmatter?.enableToc ?? this.opts.showByDefault
-        if (display) {
-          const toc: TocEntry[] = []
-          let highestDepth: number = this.opts.maxDepth
-          visit(tree, 'heading', (node) => {
-            if (node.depth <= this.opts.maxDepth) {
-              const text = toString(node)
-              highestDepth = Math.min(highestDepth, node.depth)
-              toc.push({
-                depth: node.depth,
-                text,
-                slug: slugAnchor.slug(text)
-              })
+            if (toc.length > opts.minEntries) {
+              file.data.toc = toc.map(entry => ({ ...entry, depth: entry.depth - highestDepth }))
             }
-          })
-
-          if (toc.length > this.opts.minEntries) {
-            file.data.toc = toc.map(entry => ({ ...entry, depth: entry.depth - highestDepth }))
           }
         }
-      }
-    }]
-  }
-
-  htmlPlugins(): PluggableList {
-    return []
+      }]
+    },
+    htmlPlugins() {
+      return []
+    }
   }
 }
 
diff --git a/quartz/plugins/types.ts b/quartz/plugins/types.ts
index 11b07db..ac386c9 100644
--- a/quartz/plugins/types.ts
+++ b/quartz/plugins/types.ts
@@ -4,16 +4,32 @@
 import { GlobalConfiguration } from "../cfg"
 import { QuartzComponent } from "../components/types"
 
-export abstract class QuartzTransformerPlugin {
-  abstract name: string
-  abstract markdownPlugins(): PluggableList
-  abstract htmlPlugins(): PluggableList
+export interface PluginTypes {
+  transformers: QuartzTransformerPluginInstance[],
+  filters: QuartzFilterPluginInstance[],
+  emitters: QuartzEmitterPluginInstance[],
+}
+
+type OptionType = object | undefined
+export type QuartzTransformerPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzTransformerPluginInstance
+export type QuartzTransformerPluginInstance = {
+  name: string
+  markdownPlugins(): PluggableList
+  htmlPlugins(): PluggableList
   externalResources?: Partial<StaticResources>
 }
 
-export abstract class QuartzFilterPlugin {
-  abstract name: string
-  abstract shouldPublish(content: ProcessedContent): boolean
+export type QuartzFilterPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzFilterPluginInstance 
+export type QuartzFilterPluginInstance = {
+  name: string
+  shouldPublish(content: ProcessedContent): boolean
+}
+
+export type QuartzEmitterPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzEmitterPluginInstance 
+export type QuartzEmitterPluginInstance = {
+  name: string
+  emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
+  getQuartzComponents(): QuartzComponent[]
 }
 
 export interface EmitOptions {
@@ -23,14 +39,3 @@
 }
 
 export type EmitCallback = (data: EmitOptions) => Promise<string>
-export abstract class QuartzEmitterPlugin {
-  abstract name: string
-  abstract emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
-  abstract getQuartzComponents(): QuartzComponent[]
-}
-
-export interface PluginTypes {
-  transformers: QuartzTransformerPlugin[],
-  filters: QuartzFilterPlugin[],
-  emitters: QuartzEmitterPlugin[],
-}
diff --git a/quartz/processors/filter.ts b/quartz/processors/filter.ts
index 3152a04..04c14a6 100644
--- a/quartz/processors/filter.ts
+++ b/quartz/processors/filter.ts
@@ -1,8 +1,8 @@
 import { PerfTimer } from "../perf"
-import { QuartzFilterPlugin } from "../plugins/types"
+import { QuartzFilterPluginInstance } from "../plugins/types"
 import { ProcessedContent } from "../plugins/vfile"
 
-export function filterContent(plugins: QuartzFilterPlugin[], content: ProcessedContent[], verbose: boolean): ProcessedContent[] {
+export function filterContent(plugins: QuartzFilterPluginInstance[], content: ProcessedContent[], verbose: boolean): ProcessedContent[] {
   const perf = new PerfTimer()
   const initialLength = content.length
   for (const plugin of plugins) {
diff --git a/quartz/processors/parse.ts b/quartz/processors/parse.ts
index 4fc13f8..f937701 100644
--- a/quartz/processors/parse.ts
+++ b/quartz/processors/parse.ts
@@ -11,12 +11,12 @@
 import path from 'path'
 import os from 'os'
 import workerpool, { Promise as WorkerPromise } from 'workerpool'
-import { QuartzTransformerPlugin } from '../plugins/types'
+import { QuartzTransformerPluginInstance } from '../plugins/types'
 import { QuartzLogger } from '../log'
 import chalk from 'chalk'
 
 export type QuartzProcessor = Processor<MDRoot, HTMLRoot, void>
-export function createProcessor(transformers: QuartzTransformerPlugin[]): QuartzProcessor {
+export function createProcessor(transformers: QuartzTransformerPluginInstance[]): QuartzProcessor {
   // base Markdown -> MD AST
   let processor = unified().use(remarkParse)
 
@@ -101,7 +101,7 @@
   }
 }
 
-export async function parseMarkdown(transformers: QuartzTransformerPlugin[], baseDir: string, fps: string[], verbose: boolean): Promise<ProcessedContent[]> {
+export async function parseMarkdown(transformers: QuartzTransformerPluginInstance[], baseDir: string, fps: string[], verbose: boolean): Promise<ProcessedContent[]> {
   const perf = new PerfTimer()
   const log = new QuartzLogger(verbose)
 

--
Gitblit v1.10.0