refactor plugins to be functions instead of classes
This commit is contained in:
		
							parent
							
								
									b8c011410d
								
							
						
					
					
						commit
						352075ae81
					
				
					 20 changed files with 464 additions and 507 deletions
				
			
		
							
								
								
									
										19
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -14,7 +14,6 @@
 | 
				
			||||||
        "chalk": "^4.1.2",
 | 
					        "chalk": "^4.1.2",
 | 
				
			||||||
        "cli-spinner": "^0.2.10",
 | 
					        "cli-spinner": "^0.2.10",
 | 
				
			||||||
        "esbuild-sass-plugin": "^2.9.0",
 | 
					        "esbuild-sass-plugin": "^2.9.0",
 | 
				
			||||||
        "flamethrower-router": "^0.0.0-meme.12",
 | 
					 | 
				
			||||||
        "github-slugger": "^2.0.0",
 | 
					        "github-slugger": "^2.0.0",
 | 
				
			||||||
        "globby": "^13.1.4",
 | 
					        "globby": "^13.1.4",
 | 
				
			||||||
        "gray-matter": "^4.0.3",
 | 
					        "gray-matter": "^4.0.3",
 | 
				
			||||||
| 
						 | 
					@ -22,9 +21,12 @@
 | 
				
			||||||
        "hast-util-to-string": "^2.0.0",
 | 
					        "hast-util-to-string": "^2.0.0",
 | 
				
			||||||
        "is-absolute-url": "^4.0.1",
 | 
					        "is-absolute-url": "^4.0.1",
 | 
				
			||||||
        "mdast-util-find-and-replace": "^2.2.2",
 | 
					        "mdast-util-find-and-replace": "^2.2.2",
 | 
				
			||||||
 | 
					        "mdast-util-to-string": "^3.2.0",
 | 
				
			||||||
 | 
					        "micromorph": "^0.4.5",
 | 
				
			||||||
        "preact": "^10.14.1",
 | 
					        "preact": "^10.14.1",
 | 
				
			||||||
        "preact-render-to-string": "^6.0.3",
 | 
					        "preact-render-to-string": "^6.0.3",
 | 
				
			||||||
        "pretty-time": "^1.1.0",
 | 
					        "pretty-time": "^1.1.0",
 | 
				
			||||||
 | 
					        "reading-time": "^1.5.0",
 | 
				
			||||||
        "rehype-autolink-headings": "^6.1.1",
 | 
					        "rehype-autolink-headings": "^6.1.1",
 | 
				
			||||||
        "rehype-katex": "^6.0.3",
 | 
					        "rehype-katex": "^6.0.3",
 | 
				
			||||||
        "rehype-pretty-code": "^0.9.6",
 | 
					        "rehype-pretty-code": "^0.9.6",
 | 
				
			||||||
| 
						 | 
					@ -1523,11 +1525,6 @@
 | 
				
			||||||
        "node": ">=8"
 | 
					        "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": {
 | 
					    "node_modules/foreground-child": {
 | 
				
			||||||
      "version": "3.1.1",
 | 
					      "version": "3.1.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
 | 
				
			||||||
| 
						 | 
					@ -3006,6 +3003,11 @@
 | 
				
			||||||
        "node": ">=8.6"
 | 
					        "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": {
 | 
					    "node_modules/mime-db": {
 | 
				
			||||||
      "version": "1.33.0",
 | 
					      "version": "1.33.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
 | 
				
			||||||
| 
						 | 
					@ -3268,6 +3270,11 @@
 | 
				
			||||||
        "node": ">=8.10.0"
 | 
					        "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": {
 | 
					    "node_modules/rehype-autolink-headings": {
 | 
				
			||||||
      "version": "6.1.1",
 | 
					      "version": "6.1.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-6.1.1.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,6 @@
 | 
				
			||||||
    "chalk": "^4.1.2",
 | 
					    "chalk": "^4.1.2",
 | 
				
			||||||
    "cli-spinner": "^0.2.10",
 | 
					    "cli-spinner": "^0.2.10",
 | 
				
			||||||
    "esbuild-sass-plugin": "^2.9.0",
 | 
					    "esbuild-sass-plugin": "^2.9.0",
 | 
				
			||||||
    "flamethrower-router": "^0.0.0-meme.12",
 | 
					 | 
				
			||||||
    "github-slugger": "^2.0.0",
 | 
					    "github-slugger": "^2.0.0",
 | 
				
			||||||
    "globby": "^13.1.4",
 | 
					    "globby": "^13.1.4",
 | 
				
			||||||
    "gray-matter": "^4.0.3",
 | 
					    "gray-matter": "^4.0.3",
 | 
				
			||||||
| 
						 | 
					@ -38,9 +37,12 @@
 | 
				
			||||||
    "hast-util-to-string": "^2.0.0",
 | 
					    "hast-util-to-string": "^2.0.0",
 | 
				
			||||||
    "is-absolute-url": "^4.0.1",
 | 
					    "is-absolute-url": "^4.0.1",
 | 
				
			||||||
    "mdast-util-find-and-replace": "^2.2.2",
 | 
					    "mdast-util-find-and-replace": "^2.2.2",
 | 
				
			||||||
 | 
					    "mdast-util-to-string": "^3.2.0",
 | 
				
			||||||
 | 
					    "micromorph": "^0.4.5",
 | 
				
			||||||
    "preact": "^10.14.1",
 | 
					    "preact": "^10.14.1",
 | 
				
			||||||
    "preact-render-to-string": "^6.0.3",
 | 
					    "preact-render-to-string": "^6.0.3",
 | 
				
			||||||
    "pretty-time": "^1.1.0",
 | 
					    "pretty-time": "^1.1.0",
 | 
				
			||||||
 | 
					    "reading-time": "^1.5.0",
 | 
				
			||||||
    "rehype-autolink-headings": "^6.1.1",
 | 
					    "rehype-autolink-headings": "^6.1.1",
 | 
				
			||||||
    "rehype-katex": "^6.0.3",
 | 
					    "rehype-katex": "^6.0.3",
 | 
				
			||||||
    "rehype-pretty-code": "^0.9.6",
 | 
					    "rehype-pretty-code": "^0.9.6",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,23 +39,23 @@ const config: QuartzConfig = {
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  plugins: {
 | 
					  plugins: {
 | 
				
			||||||
    transformers: [
 | 
					    transformers: [
 | 
				
			||||||
      new Plugin.FrontMatter(),
 | 
					      Plugin.FrontMatter(),
 | 
				
			||||||
      new Plugin.Description(),
 | 
					      Plugin.Description(),
 | 
				
			||||||
      new Plugin.TableOfContents({ showByDefault: true }),
 | 
					      Plugin.TableOfContents({ showByDefault: true }),
 | 
				
			||||||
      new Plugin.CreatedModifiedDate({
 | 
					      Plugin.CreatedModifiedDate({
 | 
				
			||||||
        priority: ['frontmatter', 'filesystem'] // you can add 'git' here for last modified from Git but this makes the build slower
 | 
					        priority: ['frontmatter', 'filesystem'] // you can add 'git' here for last modified from Git but this makes the build slower
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      new Plugin.GitHubFlavoredMarkdown(),
 | 
					      Plugin.GitHubFlavoredMarkdown(),
 | 
				
			||||||
      new Plugin.ObsidianFlavoredMarkdown(),
 | 
					      Plugin.ObsidianFlavoredMarkdown(),
 | 
				
			||||||
      new Plugin.ResolveLinks(),
 | 
					      Plugin.ResolveLinks(),
 | 
				
			||||||
      new Plugin.SyntaxHighlighting(),
 | 
					      Plugin.SyntaxHighlighting(),
 | 
				
			||||||
      new Plugin.Katex(),
 | 
					      Plugin.Katex(),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    filters: [
 | 
					    filters: [
 | 
				
			||||||
      new Plugin.RemoveDrafts()
 | 
					      Plugin.RemoveDrafts()
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    emitters: [
 | 
					    emitters: [
 | 
				
			||||||
      new Plugin.ContentPage({
 | 
					      Plugin.ContentPage({
 | 
				
			||||||
        head: Component.Head,
 | 
					        head: Component.Head,
 | 
				
			||||||
        header: [Component.PageTitle, Component.Spacer, Component.Darkmode],
 | 
					        header: [Component.PageTitle, Component.Spacer, Component.Darkmode],
 | 
				
			||||||
        body: [Component.ArticleTitle, Component.ReadingTime, Component.TableOfContents, Component.Content]
 | 
					        body: [Component.ArticleTitle, Component.ReadingTime, Component.TableOfContents, Component.Content]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,19 @@
 | 
				
			||||||
import { QuartzComponentProps } from "./types"
 | 
					import { QuartzComponentProps } from "./types"
 | 
				
			||||||
import style from "./styles/toc.scss"
 | 
					import style from "./styles/toc.scss"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function TableOfContents({ fileData, position }: QuartzComponentProps) {
 | 
					export default function TableOfContents({ fileData }: QuartzComponentProps) {
 | 
				
			||||||
  if (!fileData.toc) {
 | 
					  if (!fileData.toc) {
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (position === 'body') {
 | 
					  return <details class="toc" open>
 | 
				
			||||||
    // TODO: animate this
 | 
					    <summary><h3>Table of Contents</h3></summary>
 | 
				
			||||||
    return <details className="toc" open>
 | 
					    <ul>
 | 
				
			||||||
      <summary><h3>Table of Contents</h3></summary>
 | 
					      {fileData.toc.map(tocEntry => <li key={tocEntry.slug} class={`depth-${tocEntry.depth}`}>
 | 
				
			||||||
      <ul>
 | 
					        <a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
 | 
				
			||||||
        {fileData.toc.map(tocEntry => <li key={tocEntry.slug} className={`depth-${tocEntry.depth}`}>
 | 
					      </li>)}
 | 
				
			||||||
          <a href={`#${tocEntry.slug}`}>{tocEntry.text}</a>
 | 
					    </ul>
 | 
				
			||||||
        </li>)}
 | 
					  </details>
 | 
				
			||||||
      </ul>
 | 
					 | 
				
			||||||
    </details>
 | 
					 | 
				
			||||||
  } else if (position === 'sidebar') {
 | 
					 | 
				
			||||||
    // TODO
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TableOfContents.css = style
 | 
					TableOfContents.css = style
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ export type QuartzComponentProps = {
 | 
				
			||||||
  cfg: GlobalConfiguration
 | 
					  cfg: GlobalConfiguration
 | 
				
			||||||
  children: QuartzComponent[] | JSX.Element[]
 | 
					  children: QuartzComponent[] | JSX.Element[]
 | 
				
			||||||
  tree: Node<QuartzPluginData>
 | 
					  tree: Node<QuartzPluginData>
 | 
				
			||||||
  position?: 'sidebar' | 'header' | 'body'
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type QuartzComponent = ComponentType<QuartzComponentProps> & {
 | 
					export type QuartzComponent = ComponentType<QuartzComponentProps> & {
 | 
				
			||||||
| 
						 | 
					@ -18,3 +17,5 @@ export type QuartzComponent = ComponentType<QuartzComponentProps> & {
 | 
				
			||||||
  beforeDOMLoaded?: string,
 | 
					  beforeDOMLoaded?: string,
 | 
				
			||||||
  afterDOMLoaded?: string,
 | 
					  afterDOMLoaded?: string,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type QuartzComponentConstructor<Options extends object> = (opts: Options) => QuartzComponent
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,66 +15,64 @@ interface Options {
 | 
				
			||||||
  body: QuartzComponent[]
 | 
					  body: QuartzComponent[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ContentPage extends QuartzEmitterPlugin {
 | 
					export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
 | 
				
			||||||
  name = "ContentPage"
 | 
					  if (!opts) {
 | 
				
			||||||
  opts: Options
 | 
					    throw new Error("ContentPage must be initialized with options specifiying the components to use")
 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(opts: Options) {
 | 
					 | 
				
			||||||
    super()
 | 
					 | 
				
			||||||
    this.opts = opts
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getQuartzComponents(): QuartzComponent[] {
 | 
					  return {
 | 
				
			||||||
    return [this.opts.head, Header, ...this.opts.header, ...this.opts.body]
 | 
					    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 { head: Head, header, body } = opts
 | 
				
			||||||
    const fps: string[] = []
 | 
					      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
 | 
					        const componentData: QuartzComponentProps = {
 | 
				
			||||||
    for (const [tree, file] of content) {
 | 
					          fileData: file.data,
 | 
				
			||||||
      const baseDir = resolveToRoot(file.data.slug!)
 | 
					          externalResources: pageResources,
 | 
				
			||||||
      const pageResources: StaticResources = {
 | 
					          cfg,
 | 
				
			||||||
        css: [baseDir + "/index.css", ...resources.css],
 | 
					          children: [],
 | 
				
			||||||
        js: [
 | 
					          tree
 | 
				
			||||||
          { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady" },
 | 
					        }
 | 
				
			||||||
          ...resources.js,
 | 
					
 | 
				
			||||||
          { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", type: 'module' }
 | 
					        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)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      return fps
 | 
				
			||||||
      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
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,9 @@
 | 
				
			||||||
import { QuartzFilterPlugin } from "../types"
 | 
					import { QuartzFilterPlugin } from "../types"
 | 
				
			||||||
import { ProcessedContent } from "../vfile"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class RemoveDrafts extends QuartzFilterPlugin {
 | 
					export const RemoveDrafts: QuartzFilterPlugin<{}> = () => ({
 | 
				
			||||||
  name = "RemoveDrafts"
 | 
					  name: "RemoveDrafts",
 | 
				
			||||||
  shouldPublish([_tree, vfile]: ProcessedContent): boolean {
 | 
					  shouldPublish([_tree, vfile]) {
 | 
				
			||||||
    const draftFlag: boolean = vfile.data?.frontmatter?.draft ?? false
 | 
					    const draftFlag: boolean = vfile.data?.frontmatter?.draft ?? false
 | 
				
			||||||
    return !draftFlag
 | 
					    return !draftFlag
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,9 @@
 | 
				
			||||||
import { QuartzFilterPlugin } from "../types"
 | 
					import { QuartzFilterPlugin } from "../types"
 | 
				
			||||||
import { ProcessedContent } from "../vfile"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ExplicitPublish extends QuartzFilterPlugin {
 | 
					export const ExplicitPublish: QuartzFilterPlugin = () => ({
 | 
				
			||||||
  name = "ExplicitPublish"
 | 
					  name: "ExplicitPublish",
 | 
				
			||||||
  shouldPublish([_tree, vfile]: ProcessedContent): boolean {
 | 
					  shouldPublish([_tree, vfile]) {
 | 
				
			||||||
    const publishFlag: boolean = vfile.data?.frontmatter?.publish ?? false
 | 
					    const publishFlag: boolean = vfile.data?.frontmatter?.publish ?? false
 | 
				
			||||||
    return publishFlag
 | 
					    return publishFlag
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import { Root as HTMLRoot } from 'hast'
 | 
					import { Root as HTMLRoot } from 'hast'
 | 
				
			||||||
import { toString } from "hast-util-to-string"
 | 
					import { toString } from "hast-util-to-string"
 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
| 
						 | 
					@ -11,41 +10,36 @@ const defaultOptions: Options = {
 | 
				
			||||||
  descriptionLength: 150
 | 
					  descriptionLength: 150
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Description extends QuartzTransformerPlugin {
 | 
					export const Description: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
 | 
				
			||||||
  name = "Description"
 | 
					  const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
  opts: Options
 | 
					  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>) {
 | 
					            const desc = frontMatterDescription ?? text
 | 
				
			||||||
    super()
 | 
					            const sentences = desc.replace(/\s+/g, ' ').split('.')
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					            let finalDesc = ""
 | 
				
			||||||
  }
 | 
					            let sentenceIdx = 0
 | 
				
			||||||
 | 
					            const len = opts.descriptionLength
 | 
				
			||||||
 | 
					            while (finalDesc.length < len) {
 | 
				
			||||||
 | 
					              finalDesc += sentences[sentenceIdx] + '.'
 | 
				
			||||||
 | 
					              sentenceIdx++
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  markdownPlugins(): PluggableList {
 | 
					            file.data.description = finalDesc
 | 
				
			||||||
    return []
 | 
					            file.data.text = text
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      ]
 | 
				
			||||||
    ]
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import matter from "gray-matter"
 | 
					import matter from "gray-matter"
 | 
				
			||||||
import remarkFrontmatter from 'remark-frontmatter'
 | 
					import remarkFrontmatter from 'remark-frontmatter'
 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
| 
						 | 
					@ -13,35 +12,30 @@ const defaultOptions: Options = {
 | 
				
			||||||
  delims: '---'
 | 
					  delims: '---'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class FrontMatter extends QuartzTransformerPlugin {
 | 
					export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
 | 
				
			||||||
  name = "FrontMatter"
 | 
					  const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
  opts: Options
 | 
					  return {
 | 
				
			||||||
 | 
					    name: "FrontMatter",
 | 
				
			||||||
 | 
					    markdownPlugins() {
 | 
				
			||||||
 | 
					      return [
 | 
				
			||||||
 | 
					        remarkFrontmatter,
 | 
				
			||||||
 | 
					        () => {
 | 
				
			||||||
 | 
					          return (_, file) => {
 | 
				
			||||||
 | 
					            const { data } = matter(file.value, opts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(opts?: Partial<Options>) {
 | 
					            // fill in frontmatter
 | 
				
			||||||
    super()
 | 
					            file.data.frontmatter = {
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					              title: file.stem ?? "Untitled",
 | 
				
			||||||
  }
 | 
					              tags: [],
 | 
				
			||||||
 | 
					              ...data
 | 
				
			||||||
  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
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      ]
 | 
				
			||||||
    ]
 | 
					    },
 | 
				
			||||||
  }
 | 
					    htmlPlugins() {
 | 
				
			||||||
 | 
					      return []
 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					    }
 | 
				
			||||||
    return []
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,27 +15,24 @@ const defaultOptions: Options = {
 | 
				
			||||||
  linkHeadings: true
 | 
					  linkHeadings: true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class GitHubFlavoredMarkdown extends QuartzTransformerPlugin {
 | 
					export const GitHubFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
 | 
				
			||||||
  name = "GitHubFlavoredMarkdown"
 | 
					  const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
  opts: Options
 | 
					  return {
 | 
				
			||||||
 | 
					    name: "GitHubFlavoredMarkdown",
 | 
				
			||||||
  constructor(opts?: Partial<Options>) {
 | 
					    markdownPlugins() {
 | 
				
			||||||
    super()
 | 
					      return opts.enableSmartyPants ? [remarkGfm] : [remarkGfm, smartypants]
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					    },
 | 
				
			||||||
  }
 | 
					    htmlPlugins() {
 | 
				
			||||||
 | 
					      if (opts.linkHeadings) {
 | 
				
			||||||
  markdownPlugins(): PluggableList {
 | 
					        return [rehypeSlug, [rehypeAutolinkHeadings, {
 | 
				
			||||||
    return this.opts.enableSmartyPants ? [remarkGfm] : [remarkGfm, smartypants]
 | 
					          behavior: 'append', content: {
 | 
				
			||||||
  }
 | 
					            type: 'text',
 | 
				
			||||||
 | 
					            value: ' §'
 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					          }
 | 
				
			||||||
    return this.opts.linkHeadings
 | 
					        }]]
 | 
				
			||||||
      ? [rehypeSlug, [rehypeAutolinkHeadings, {
 | 
					      } else {
 | 
				
			||||||
        behavior: 'append', content: {
 | 
					        return []
 | 
				
			||||||
          type: 'text',
 | 
					      }
 | 
				
			||||||
          value: ' §'
 | 
					    }
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }]]
 | 
					 | 
				
			||||||
      : []
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import fs from "fs"
 | 
					import fs from "fs"
 | 
				
			||||||
import path from 'path'
 | 
					import path from 'path'
 | 
				
			||||||
import { Repository } from "@napi-rs/simple-git"
 | 
					import { Repository } from "@napi-rs/simple-git"
 | 
				
			||||||
| 
						 | 
					@ -12,59 +11,51 @@ const defaultOptions: Options = {
 | 
				
			||||||
  priority: ['frontmatter', 'git', 'filesystem']
 | 
					  priority: ['frontmatter', 'git', 'filesystem']
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class CreatedModifiedDate extends QuartzTransformerPlugin {
 | 
					export const CreatedModifiedDate: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
 | 
				
			||||||
  name = "CreatedModifiedDate"
 | 
					  const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
  opts: Options
 | 
					  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>) {
 | 
					            const fp = path.join(file.cwd, file.data.filePath as string)
 | 
				
			||||||
    super()
 | 
					            for (const source of opts.priority) {
 | 
				
			||||||
    this.opts = {
 | 
					              if (source === "filesystem") {
 | 
				
			||||||
      ...defaultOptions,
 | 
					                const st = await fs.promises.stat(fp)
 | 
				
			||||||
      ...opts,
 | 
					                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 {
 | 
					                modified ||= new Date(await repo.getFileLatestModifiedDateAsync(file.data.filePath!))
 | 
				
			||||||
    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!))
 | 
					            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() {
 | 
				
			||||||
 | 
					      return []
 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					    }
 | 
				
			||||||
    return []
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,20 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import remarkMath from "remark-math"
 | 
					import remarkMath from "remark-math"
 | 
				
			||||||
import rehypeKatex from 'rehype-katex'
 | 
					import rehypeKatex from 'rehype-katex'
 | 
				
			||||||
import { StaticResources } from "../../resources"
 | 
					 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Katex extends QuartzTransformerPlugin {
 | 
					export const Katex: QuartzTransformerPlugin = () => ({
 | 
				
			||||||
  name = "Katex"
 | 
					  name: "Katex",
 | 
				
			||||||
  markdownPlugins(): PluggableList {
 | 
					  markdownPlugins() {
 | 
				
			||||||
    return [remarkMath]
 | 
					    return [remarkMath]
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
 | 
					  htmlPlugins() {
 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					 | 
				
			||||||
    return [
 | 
					    return [
 | 
				
			||||||
      [rehypeKatex, {
 | 
					      [rehypeKatex, {
 | 
				
			||||||
        output: 'html',
 | 
					        output: 'html',
 | 
				
			||||||
      }]
 | 
					      }]
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
 | 
					  externalResources: {
 | 
				
			||||||
  externalResources: Partial<StaticResources> = {
 | 
					 | 
				
			||||||
    css: [
 | 
					    css: [
 | 
				
			||||||
      // base css
 | 
					      // base css
 | 
				
			||||||
      "https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css",
 | 
					      "https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css",
 | 
				
			||||||
| 
						 | 
					@ -31,4 +27,4 @@ export class Katex extends QuartzTransformerPlugin {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
import { relative, relativeToRoot, slugify } from "../../path"
 | 
					import { relative, relativeToRoot, slugify } from "../../path"
 | 
				
			||||||
import path from "path"
 | 
					import path from "path"
 | 
				
			||||||
| 
						 | 
					@ -17,65 +16,60 @@ const defaultOptions: Options = {
 | 
				
			||||||
  prettyLinks: true
 | 
					  prettyLinks: true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ResolveLinks extends QuartzTransformerPlugin {
 | 
					export const ResolveLinks: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
 | 
				
			||||||
  name = "LinkProcessing"
 | 
					  const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
  opts: Options
 | 
					  return {
 | 
				
			||||||
 | 
					    name: "LinkProcessing",
 | 
				
			||||||
  constructor(opts?: Partial<Options>) {
 | 
					    markdownPlugins() {
 | 
				
			||||||
    super()
 | 
					      return []
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					    },
 | 
				
			||||||
  }
 | 
					    htmlPlugins() {
 | 
				
			||||||
 | 
					      return [() => {
 | 
				
			||||||
  markdownPlugins(): PluggableList {
 | 
					        return (tree, file) => {
 | 
				
			||||||
    return []
 | 
					          const curSlug = file.data.slug!
 | 
				
			||||||
  }
 | 
					          const transformLink = (target: string) => {
 | 
				
			||||||
 | 
					            const targetSlug = slugify(decodeURI(target).trim())
 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					            if (opts.markdownLinkResolution === 'relative' && !path.isAbsolute(targetSlug)) {
 | 
				
			||||||
    return [() => {
 | 
					              return './' + relative(curSlug, targetSlug)
 | 
				
			||||||
      return (tree, file) => {
 | 
					            } else {
 | 
				
			||||||
        const curSlug = file.data.slug!
 | 
					              return './' + relativeToRoot(curSlug, targetSlug)
 | 
				
			||||||
        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)
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          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
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }]
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,174 +89,168 @@ const capitalize = (s: string): string => {
 | 
				
			||||||
  return s.substring(0, 1).toUpperCase() + s.substring(1);
 | 
					  return s.substring(0, 1).toUpperCase() + s.substring(1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
 | 
					export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
 | 
				
			||||||
  name = "ObsidianFlavoredMarkdown"
 | 
					  const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
  opts: Options
 | 
					  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>) {
 | 
					              // embed cases
 | 
				
			||||||
    super()
 | 
					              if (value.startsWith("!")) {
 | 
				
			||||||
    this.opts = { ...defaultOptions, ...opts }
 | 
					                const ext = path.extname(fp).toLowerCase()
 | 
				
			||||||
  }
 | 
					                const url = slugify(fp.trim()) + ext
 | 
				
			||||||
 | 
					                if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg"].includes(ext)) {
 | 
				
			||||||
  markdownPlugins(): PluggableList {
 | 
					                  const dims = alias ?? ""
 | 
				
			||||||
    const plugins: PluggableList = []
 | 
					                  let [width, height] = dims.split("x", 2)
 | 
				
			||||||
 | 
					                  width ||= "auto"
 | 
				
			||||||
    if (this.opts.wikilinks) {
 | 
					                  height ||= "auto"
 | 
				
			||||||
      plugins.push(() => {
 | 
					                  return {
 | 
				
			||||||
        // Match wikilinks 
 | 
					                    type: 'image',
 | 
				
			||||||
        // !?               -> optional embedding
 | 
					                    url,
 | 
				
			||||||
        // \[\[             -> open brace
 | 
					                    data: {
 | 
				
			||||||
        // ([^\[\]\|\#]+)   -> one or more non-special characters ([,],|, or #) (name)
 | 
					                      hProperties: {
 | 
				
			||||||
        // (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
 | 
					                        width, height
 | 
				
			||||||
        // (|[^\[\]\|\#]+)? -> | 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
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
 | 
					                } 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)) {
 | 
					                // otherwise, fall through to regular link
 | 
				
			||||||
                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
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // internal link
 | 
					              // internal link
 | 
				
			||||||
            const url = slugify(fp.trim() + anchor)
 | 
					              const url = slugify(fp.trim() + anchor)
 | 
				
			||||||
            return {
 | 
					              return {
 | 
				
			||||||
              type: 'link',
 | 
					                type: 'link',
 | 
				
			||||||
              url,
 | 
					                url,
 | 
				
			||||||
              children: [{
 | 
					                children: [{
 | 
				
			||||||
                type: 'text',
 | 
					                  type: 'text',
 | 
				
			||||||
                value: alias ?? fp
 | 
					                  value: alias ?? fp
 | 
				
			||||||
              }]
 | 
					                }]
 | 
				
			||||||
            }
 | 
					              }
 | 
				
			||||||
          })
 | 
					            })
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.opts.highlight) {
 | 
					      if (opts.highlight) {
 | 
				
			||||||
      plugins.push(() => {
 | 
					        plugins.push(() => {
 | 
				
			||||||
        // Match highlights 
 | 
					          // Match highlights 
 | 
				
			||||||
        const highlightRegex = new RegExp(/==(.+)==/, "g")
 | 
					          const highlightRegex = new RegExp(/==(.+)==/, "g")
 | 
				
			||||||
        return (tree: Root, _file) => {
 | 
					          return (tree: Root, _file) => {
 | 
				
			||||||
          findAndReplace(tree, highlightRegex, (_value: string, ...capture: string[]) => {
 | 
					            findAndReplace(tree, highlightRegex, (_value: string, ...capture: string[]) => {
 | 
				
			||||||
            const [inner] = capture
 | 
					              const [inner] = capture
 | 
				
			||||||
            return {
 | 
					              return {
 | 
				
			||||||
              type: 'html',
 | 
					                type: 'html',
 | 
				
			||||||
              value: `<span class="text-highlight">${inner}</span>`
 | 
					                value: `<span class="text-highlight">${inner}</span>`
 | 
				
			||||||
            }
 | 
					              }
 | 
				
			||||||
          })
 | 
					            })
 | 
				
			||||||
        }
 | 
					          }
 | 
				
			||||||
      })
 | 
					        })
 | 
				
			||||||
    }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.opts.callouts) {
 | 
					      if (opts.callouts) {
 | 
				
			||||||
      plugins.push(() => {
 | 
					        plugins.push(() => {
 | 
				
			||||||
        // from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts
 | 
					          // from https://github.com/escwxyz/remark-obsidian-callout/blob/main/src/index.ts
 | 
				
			||||||
        const calloutRegex = new RegExp(/^\[\!(\w+)\]([+-]?)/)
 | 
					          const calloutRegex = new RegExp(/^\[\!(\w+)\]([+-]?)/)
 | 
				
			||||||
        return (tree: Root, _file) => {
 | 
					          return (tree: Root, _file) => {
 | 
				
			||||||
          visit(tree, "blockquote", (node) => {
 | 
					            visit(tree, "blockquote", (node) => {
 | 
				
			||||||
            if (node.children.length === 0) {
 | 
					              if (node.children.length === 0) {
 | 
				
			||||||
              return
 | 
					                return
 | 
				
			||||||
            }
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // find first line
 | 
					              // find first line
 | 
				
			||||||
            const firstChild = node.children[0]
 | 
					              const firstChild = node.children[0]
 | 
				
			||||||
            if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
 | 
					              if (firstChild.type !== "paragraph" || firstChild.children[0]?.type !== "text") {
 | 
				
			||||||
              return
 | 
					                return
 | 
				
			||||||
            }
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const text = firstChild.children[0].value
 | 
					              const text = firstChild.children[0].value
 | 
				
			||||||
            const [firstLine, ...remainingLines] = text.split("\n")
 | 
					              const [firstLine, ...remainingLines] = text.split("\n")
 | 
				
			||||||
            const remainingText = remainingLines.join("\n")
 | 
					              const remainingText = remainingLines.join("\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const match = firstLine.match(calloutRegex)
 | 
					              const match = firstLine.match(calloutRegex)
 | 
				
			||||||
            if (match && match.input) {
 | 
					              if (match && match.input) {
 | 
				
			||||||
              const [calloutDirective, typeString, collapseChar] = match
 | 
					                const [calloutDirective, typeString, collapseChar] = match
 | 
				
			||||||
              const calloutType = typeString.toLowerCase() as keyof typeof callouts
 | 
					                const calloutType = typeString.toLowerCase() as keyof typeof callouts
 | 
				
			||||||
              const collapse = collapseChar === "+" || collapseChar === "-"
 | 
					                const collapse = collapseChar === "+" || collapseChar === "-"
 | 
				
			||||||
              const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
 | 
					                const defaultState = collapseChar === "-" ? "collapsed" : "expanded"
 | 
				
			||||||
              const title = match.input.slice(calloutDirective.length).trim() || capitalize(calloutType)
 | 
					                const title = match.input.slice(calloutDirective.length).trim() || capitalize(calloutType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              const titleNode: HTML = {
 | 
					                const titleNode: HTML = {
 | 
				
			||||||
                type: "html",
 | 
					                  type: "html",
 | 
				
			||||||
                value: `<div 
 | 
					                  value: `<div 
 | 
				
			||||||
                  class="callout-title"
 | 
					                  class="callout-title"
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                  <div class="callout-icon">${callouts[canonicalizeCallout(calloutType)]}</div>
 | 
					                  <div class="callout-icon">${callouts[canonicalizeCallout(calloutType)]}</div>
 | 
				
			||||||
                  <div class="callout-title-inner">${title}</div>
 | 
					                  <div class="callout-title-inner">${title}</div>
 | 
				
			||||||
                </div>`
 | 
					                </div>`
 | 
				
			||||||
              }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              const blockquoteContent: (BlockContent | DefinitionContent)[] = [titleNode]
 | 
					                const blockquoteContent: (BlockContent | DefinitionContent)[] = [titleNode]
 | 
				
			||||||
              if (remainingText.length > 0) {
 | 
					                if (remainingText.length > 0) {
 | 
				
			||||||
                blockquoteContent.push({
 | 
					                  blockquoteContent.push({
 | 
				
			||||||
                  type: 'paragraph',
 | 
					                    type: 'paragraph',
 | 
				
			||||||
                  children: [{
 | 
					                    children: [{
 | 
				
			||||||
                    type: 'text',
 | 
					                      type: 'text',
 | 
				
			||||||
                    value: remainingText,
 | 
					                      value: remainingText,
 | 
				
			||||||
                  }]
 | 
					                    }]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                })
 | 
					                  })
 | 
				
			||||||
              }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              // replace first line of blockquote with title and rest of the paragraph text
 | 
					                // replace first line of blockquote with title and rest of the paragraph text
 | 
				
			||||||
              node.children.splice(0, 1, ...blockquoteContent)
 | 
					                node.children.splice(0, 1, ...blockquoteContent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              // add properties to base blockquote
 | 
					                // add properties to base blockquote
 | 
				
			||||||
              node.data = {
 | 
					                node.data = {
 | 
				
			||||||
                hProperties: {
 | 
					                  hProperties: {
 | 
				
			||||||
                  ...(node.data?.hProperties ?? {}),
 | 
					                    ...(node.data?.hProperties ?? {}),
 | 
				
			||||||
                  className: `callout ${collapse ? "is-collapsible" : ""} ${defaultState === "collapsed" ? "is-collapsed" : ""}`,
 | 
					                    className: `callout ${collapse ? "is-collapsible" : ""} ${defaultState === "collapsed" ? "is-collapsed" : ""}`,
 | 
				
			||||||
                  "data-callout": calloutType,
 | 
					                    "data-callout": calloutType,
 | 
				
			||||||
                  "data-callout-fold": collapse,
 | 
					                    "data-callout-fold": collapse,
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            })
 | 
				
			||||||
          })
 | 
					          }
 | 
				
			||||||
        }
 | 
					        })
 | 
				
			||||||
      })
 | 
					      }
 | 
				
			||||||
 | 
					      return plugins
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    htmlPlugins() {
 | 
				
			||||||
 | 
					      return [rehypeRaw]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return plugins
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					 | 
				
			||||||
    return [rehypeRaw]
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,12 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
import rehypePrettyCode, { Options as CodeOptions } from "rehype-pretty-code"
 | 
					import rehypePrettyCode, { Options as CodeOptions } from "rehype-pretty-code"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SyntaxHighlighting extends QuartzTransformerPlugin {
 | 
					export const SyntaxHighlighting: QuartzTransformerPlugin = () => ({
 | 
				
			||||||
  name = "SyntaxHighlighting"
 | 
					  name: "SyntaxHighlighting",
 | 
				
			||||||
 | 
					  markdownPlugins() {
 | 
				
			||||||
  markdownPlugins(): PluggableList {
 | 
					 | 
				
			||||||
    return []
 | 
					    return []
 | 
				
			||||||
  }
 | 
					  },
 | 
				
			||||||
 | 
					  htmlPlugins() {
 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					 | 
				
			||||||
    return [[rehypePrettyCode, {
 | 
					    return [[rehypePrettyCode, {
 | 
				
			||||||
      theme: 'css-variables',
 | 
					      theme: 'css-variables',
 | 
				
			||||||
      onVisitLine(node) {
 | 
					      onVisitLine(node) {
 | 
				
			||||||
| 
						 | 
					@ -25,4 +22,4 @@ export class SyntaxHighlighting extends QuartzTransformerPlugin {
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    } satisfies Partial<CodeOptions>]]
 | 
					    } satisfies Partial<CodeOptions>]]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import { PluggableList } from "unified"
 | 
					 | 
				
			||||||
import { QuartzTransformerPlugin } from "../types"
 | 
					import { QuartzTransformerPlugin } from "../types"
 | 
				
			||||||
import { Root } from "mdast"
 | 
					import { Root } from "mdast"
 | 
				
			||||||
import { visit } from "unist-util-visit"
 | 
					import { visit } from "unist-util-visit"
 | 
				
			||||||
| 
						 | 
					@ -23,44 +22,39 @@ interface TocEntry {
 | 
				
			||||||
  slug: string
 | 
					  slug: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class TableOfContents extends QuartzTransformerPlugin {
 | 
					export const TableOfContents: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
 | 
				
			||||||
  name = "TableOfContents"
 | 
					  const opts = { ...defaultOptions, ...userOpts }
 | 
				
			||||||
  opts: Options
 | 
					  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>) {
 | 
					            if (toc.length > opts.minEntries) {
 | 
				
			||||||
    super()
 | 
					              file.data.toc = toc.map(entry => ({ ...entry, depth: entry.depth - highestDepth }))
 | 
				
			||||||
    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 > this.opts.minEntries) {
 | 
					 | 
				
			||||||
            file.data.toc = toc.map(entry => ({ ...entry, depth: entry.depth - highestDepth }))
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }]
 | 
				
			||||||
    }]
 | 
					    },
 | 
				
			||||||
  }
 | 
					    htmlPlugins() {
 | 
				
			||||||
 | 
					      return []
 | 
				
			||||||
  htmlPlugins(): PluggableList {
 | 
					    }
 | 
				
			||||||
    return []
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,16 +4,32 @@ import { ProcessedContent } from "./vfile"
 | 
				
			||||||
import { GlobalConfiguration } from "../cfg"
 | 
					import { GlobalConfiguration } from "../cfg"
 | 
				
			||||||
import { QuartzComponent } from "../components/types"
 | 
					import { QuartzComponent } from "../components/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export abstract class QuartzTransformerPlugin {
 | 
					export interface PluginTypes {
 | 
				
			||||||
  abstract name: string
 | 
					  transformers: QuartzTransformerPluginInstance[],
 | 
				
			||||||
  abstract markdownPlugins(): PluggableList
 | 
					  filters: QuartzFilterPluginInstance[],
 | 
				
			||||||
  abstract htmlPlugins(): PluggableList
 | 
					  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>
 | 
					  externalResources?: Partial<StaticResources>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export abstract class QuartzFilterPlugin {
 | 
					export type QuartzFilterPlugin<Options extends OptionType = undefined> = (opts?: Options) => QuartzFilterPluginInstance 
 | 
				
			||||||
  abstract name: string
 | 
					export type QuartzFilterPluginInstance = {
 | 
				
			||||||
  abstract shouldPublish(content: ProcessedContent): boolean
 | 
					  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 {
 | 
					export interface EmitOptions {
 | 
				
			||||||
| 
						 | 
					@ -23,14 +39,3 @@ export interface EmitOptions {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type EmitCallback = (data: EmitOptions) => Promise<string>
 | 
					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[],
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
import { PerfTimer } from "../perf"
 | 
					import { PerfTimer } from "../perf"
 | 
				
			||||||
import { QuartzFilterPlugin } from "../plugins/types"
 | 
					import { QuartzFilterPluginInstance } from "../plugins/types"
 | 
				
			||||||
import { ProcessedContent } from "../plugins/vfile"
 | 
					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 perf = new PerfTimer()
 | 
				
			||||||
  const initialLength = content.length
 | 
					  const initialLength = content.length
 | 
				
			||||||
  for (const plugin of plugins) {
 | 
					  for (const plugin of plugins) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,12 +11,12 @@ import { slugify } from '../path'
 | 
				
			||||||
import path from 'path'
 | 
					import path from 'path'
 | 
				
			||||||
import os from 'os'
 | 
					import os from 'os'
 | 
				
			||||||
import workerpool, { Promise as WorkerPromise } from 'workerpool'
 | 
					import workerpool, { Promise as WorkerPromise } from 'workerpool'
 | 
				
			||||||
import { QuartzTransformerPlugin } from '../plugins/types'
 | 
					import { QuartzTransformerPluginInstance } from '../plugins/types'
 | 
				
			||||||
import { QuartzLogger } from '../log'
 | 
					import { QuartzLogger } from '../log'
 | 
				
			||||||
import chalk from 'chalk'
 | 
					import chalk from 'chalk'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type QuartzProcessor = Processor<MDRoot, HTMLRoot, void>
 | 
					export type QuartzProcessor = Processor<MDRoot, HTMLRoot, void>
 | 
				
			||||||
export function createProcessor(transformers: QuartzTransformerPlugin[]): QuartzProcessor {
 | 
					export function createProcessor(transformers: QuartzTransformerPluginInstance[]): QuartzProcessor {
 | 
				
			||||||
  // base Markdown -> MD AST
 | 
					  // base Markdown -> MD AST
 | 
				
			||||||
  let processor = unified().use(remarkParse)
 | 
					  let processor = unified().use(remarkParse)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,7 +101,7 @@ export function createFileParser(baseDir: string, fps: string[], verbose: boolea
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 perf = new PerfTimer()
 | 
				
			||||||
  const log = new QuartzLogger(verbose)
 | 
					  const log = new QuartzLogger(verbose)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue