inline scripts
This commit is contained in:
		
							parent
							
								
									fcd81353f8
								
							
						
					
					
						commit
						4bdc17d4a1
					
				
					 19 changed files with 187 additions and 69 deletions
				
			
		
							
								
								
									
										12
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -13,6 +13,7 @@
 | 
				
			||||||
        "@napi-rs/simple-git": "^0.1.8",
 | 
					        "@napi-rs/simple-git": "^0.1.8",
 | 
				
			||||||
        "chalk": "^4.1.2",
 | 
					        "chalk": "^4.1.2",
 | 
				
			||||||
        "cli-spinner": "^0.2.10",
 | 
					        "cli-spinner": "^0.2.10",
 | 
				
			||||||
 | 
					        "env-paths": "^3.0.0",
 | 
				
			||||||
        "esbuild-sass-plugin": "^2.9.0",
 | 
					        "esbuild-sass-plugin": "^2.9.0",
 | 
				
			||||||
        "github-slugger": "^2.0.0",
 | 
					        "github-slugger": "^2.0.0",
 | 
				
			||||||
        "globby": "^13.1.4",
 | 
					        "globby": "^13.1.4",
 | 
				
			||||||
| 
						 | 
					@ -1346,6 +1347,17 @@
 | 
				
			||||||
        "url": "https://github.com/fb55/entities?sponsor=1"
 | 
					        "url": "https://github.com/fb55/entities?sponsor=1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/env-paths": {
 | 
				
			||||||
 | 
					      "version": "3.0.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "url": "https://github.com/sponsors/sindresorhus"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/esbuild": {
 | 
					    "node_modules/esbuild": {
 | 
				
			||||||
      "version": "0.17.19",
 | 
					      "version": "0.17.19",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,7 @@
 | 
				
			||||||
    "@napi-rs/simple-git": "^0.1.8",
 | 
					    "@napi-rs/simple-git": "^0.1.8",
 | 
				
			||||||
    "chalk": "^4.1.2",
 | 
					    "chalk": "^4.1.2",
 | 
				
			||||||
    "cli-spinner": "^0.2.10",
 | 
					    "cli-spinner": "^0.2.10",
 | 
				
			||||||
 | 
					    "env-paths": "^3.0.0",
 | 
				
			||||||
    "esbuild-sass-plugin": "^2.9.0",
 | 
					    "esbuild-sass-plugin": "^2.9.0",
 | 
				
			||||||
    "github-slugger": "^2.0.0",
 | 
					    "github-slugger": "^2.0.0",
 | 
				
			||||||
    "globby": "^13.1.4",
 | 
					    "globby": "^13.1.4",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
#!/usr/bin/env node
 | 
					#!/usr/bin/env node
 | 
				
			||||||
import { readFileSync } from 'fs'
 | 
					import { promises, readFileSync } from 'fs'
 | 
				
			||||||
import yargs from 'yargs'
 | 
					import yargs from 'yargs'
 | 
				
			||||||
 | 
					import path from 'path'
 | 
				
			||||||
import { hideBin } from 'yargs/helpers'
 | 
					import { hideBin } from 'yargs/helpers'
 | 
				
			||||||
import esbuild from 'esbuild'
 | 
					import esbuild from 'esbuild'
 | 
				
			||||||
import chalk from 'chalk'
 | 
					import chalk from 'chalk'
 | 
				
			||||||
| 
						 | 
					@ -61,9 +62,34 @@ yargs(hideBin(process.argv))
 | 
				
			||||||
      jsx: "automatic",
 | 
					      jsx: "automatic",
 | 
				
			||||||
      jsxImportSource: "preact",
 | 
					      jsxImportSource: "preact",
 | 
				
			||||||
      external: ["@napi-rs/simple-git", "shiki"],
 | 
					      external: ["@napi-rs/simple-git", "shiki"],
 | 
				
			||||||
      plugins: [sassPlugin({
 | 
					      plugins: [
 | 
				
			||||||
        type: 'css-text'
 | 
					        sassPlugin({
 | 
				
			||||||
      })]
 | 
					          type: 'css-text'
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          name: 'inline-script-loader',
 | 
				
			||||||
 | 
					          setup(build) {
 | 
				
			||||||
 | 
					            build.onLoad({ filter: /\.inline\.(ts|js)$/ }, async (args) => {
 | 
				
			||||||
 | 
					              let text = await promises.readFile(args.path, 'utf8')
 | 
				
			||||||
 | 
					              const transpiled = await esbuild.build({
 | 
				
			||||||
 | 
					                stdin: {
 | 
				
			||||||
 | 
					                  contents: text,
 | 
				
			||||||
 | 
					                  sourcefile: path.relative(path.resolve('.'), args.path),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                write: false,
 | 
				
			||||||
 | 
					                bundle: true,
 | 
				
			||||||
 | 
					                platform: "browser",
 | 
				
			||||||
 | 
					                format: "esm",
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					              const rawMod = transpiled.outputFiles[0].text
 | 
				
			||||||
 | 
					              return {
 | 
				
			||||||
 | 
					                contents: rawMod,
 | 
				
			||||||
 | 
					                loader: 'text',
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
    }).catch(err => {
 | 
					    }).catch(err => {
 | 
				
			||||||
      console.error(`${chalk.red("Couldn't parse Quartz configuration:")} ${fp}`)
 | 
					      console.error(`${chalk.red("Couldn't parse Quartz configuration:")} ${fp}`)
 | 
				
			||||||
      console.log(`Reason: ${chalk.grey(err)}`)
 | 
					      console.log(`Reason: ${chalk.grey(err)}`)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,10 +8,9 @@ export interface HeadProps {
 | 
				
			||||||
  externalResources: StaticResources
 | 
					  externalResources: StaticResources
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function({ title, description, slug, externalResources }: HeadProps) {
 | 
					export function Component({ title, description, slug, externalResources }: HeadProps) {
 | 
				
			||||||
  const { css, js } = externalResources
 | 
					  const { css, js } = externalResources
 | 
				
			||||||
  const baseDir = resolveToRoot(slug)
 | 
					  const baseDir = resolveToRoot(slug)
 | 
				
			||||||
  const stylePath = baseDir + "/index.css"
 | 
					 | 
				
			||||||
  const iconPath = baseDir + "/static/icon.png"
 | 
					  const iconPath = baseDir + "/static/icon.png"
 | 
				
			||||||
  const ogImagePath = baseDir + "/static/og-image.png"
 | 
					  const ogImagePath = baseDir + "/static/og-image.png"
 | 
				
			||||||
  return <head>
 | 
					  return <head>
 | 
				
			||||||
| 
						 | 
					@ -28,16 +27,7 @@ export default function({ title, description, slug, externalResources }: HeadPro
 | 
				
			||||||
    <meta name="generator" content="Quartz" />
 | 
					    <meta name="generator" content="Quartz" />
 | 
				
			||||||
    <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
					    <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
				
			||||||
    <link rel="preconnect" href="https://fonts.gstatic.com" />
 | 
					    <link rel="preconnect" href="https://fonts.gstatic.com" />
 | 
				
			||||||
    <link rel="stylesheet" type="text/css" href={stylePath} />
 | 
					 | 
				
			||||||
    {css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" />)}
 | 
					    {css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" />)}
 | 
				
			||||||
    {js.filter(resource => resource.loadTime === "beforeDOMReady").map(resource => <script key={resource.src} src={resource.src} />)}
 | 
					    {js.filter(resource => resource.loadTime === "beforeDOMReady").map(resource => <script key={resource.src} {...resource} />)}
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export function beforeDOMLoaded() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function onDOMLoaded() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,10 +5,10 @@ export interface HeaderProps {
 | 
				
			||||||
  slug: string
 | 
					  slug: string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function({ title, slug }: HeaderProps) {
 | 
					export function Component({ title, slug }: HeaderProps) {
 | 
				
			||||||
  const baseDir = resolveToRoot(slug)
 | 
					  const baseDir = resolveToRoot(slug)
 | 
				
			||||||
  return <header>
 | 
					  return <header>
 | 
				
			||||||
    <h1><a href={baseDir}>{title}</a></h1>
 | 
					    <h1><a href={baseDir}>{title}</a></h1>
 | 
				
			||||||
  </header>
 | 
					  </header>
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								quartz/components/scripts/darkmode.inline.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								quartz/components/scripts/darkmode.inline.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					export default "Darkmode" 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log("HELLOOOO FROM CONSOLE")
 | 
				
			||||||
							
								
								
									
										8
									
								
								quartz/components/types.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								quartz/components/types.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					import { ComponentType } from "preact"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type QuartzComponent<Props> = {
 | 
				
			||||||
 | 
					  Component: ComponentType<Props>
 | 
				
			||||||
 | 
					  css?: string,
 | 
				
			||||||
 | 
					  beforeDOMLoaded?: string,
 | 
				
			||||||
 | 
					  afterDOMLoaded?: string,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ export function buildQuartz(cfg: QuartzConfig) {
 | 
				
			||||||
    const parsedFiles = await parseMarkdown(processor, argv.directory, filePaths, argv.verbose)
 | 
					    const parsedFiles = await parseMarkdown(processor, argv.directory, filePaths, argv.verbose)
 | 
				
			||||||
    const filteredContent = filterContent(cfg.plugins.filters, parsedFiles, argv.verbose)
 | 
					    const filteredContent = filterContent(cfg.plugins.filters, parsedFiles, argv.verbose)
 | 
				
			||||||
    await emitContent(argv.directory, output, cfg, filteredContent, argv.verbose)
 | 
					    await emitContent(argv.directory, output, cfg, filteredContent, argv.verbose)
 | 
				
			||||||
    console.log(chalk.green(`Done in ${perf.timeSince()}`))
 | 
					    console.log(chalk.green(`Done processing ${fps.length} files in ${perf.timeSince()}`))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (argv.serve) {
 | 
					    if (argv.serve) {
 | 
				
			||||||
      const server = http.createServer(async (req, res) => {
 | 
					      const server = http.createServer(async (req, res) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,11 +22,15 @@ export function slugify(s: string): string {
 | 
				
			||||||
// resolve /a/b/c to ../../
 | 
					// resolve /a/b/c to ../../
 | 
				
			||||||
export function resolveToRoot(slug: string): string {
 | 
					export function resolveToRoot(slug: string): string {
 | 
				
			||||||
  let fp = slug
 | 
					  let fp = slug
 | 
				
			||||||
  if (fp.endsWith("/index")) {
 | 
					  if (fp.endsWith("index")) {
 | 
				
			||||||
    fp = fp.slice(0, -"/index".length)
 | 
					    fp = fp.slice(0, -"index".length)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return fp
 | 
					  if (fp === "") {
 | 
				
			||||||
 | 
					    return "."
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return "./" + fp
 | 
				
			||||||
    .split('/')
 | 
					    .split('/')
 | 
				
			||||||
    .filter(x => x !== '')
 | 
					    .filter(x => x !== '')
 | 
				
			||||||
    .map(_ => '..')
 | 
					    .map(_ => '..')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,17 +4,15 @@ import { EmitCallback, QuartzEmitterPlugin } from "../types"
 | 
				
			||||||
import { ProcessedContent } from "../vfile"
 | 
					import { ProcessedContent } from "../vfile"
 | 
				
			||||||
import { Fragment, jsx, jsxs } from 'preact/jsx-runtime'
 | 
					import { Fragment, jsx, jsxs } from 'preact/jsx-runtime'
 | 
				
			||||||
import { render } from "preact-render-to-string"
 | 
					import { render } from "preact-render-to-string"
 | 
				
			||||||
import { ComponentType } from "preact"
 | 
					 | 
				
			||||||
import { HeadProps } from "../../components/Head"
 | 
					import { HeadProps } from "../../components/Head"
 | 
				
			||||||
import { googleFontHref, templateThemeStyles } from "../../theme"
 | 
					 | 
				
			||||||
import { GlobalConfiguration } from "../../cfg"
 | 
					import { GlobalConfiguration } from "../../cfg"
 | 
				
			||||||
import { HeaderProps } from "../../components/Header"
 | 
					import { HeaderProps } from "../../components/Header"
 | 
				
			||||||
 | 
					import { QuartzComponent } from "../../components/types"
 | 
				
			||||||
import styles from '../../styles/base.scss'
 | 
					import { resolveToRoot } from "../../path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Options {
 | 
					interface Options {
 | 
				
			||||||
  Head: ComponentType<HeadProps>
 | 
					  Head: QuartzComponent<HeadProps>
 | 
				
			||||||
  Header: ComponentType<HeaderProps>
 | 
					  Header: QuartzComponent<HeaderProps>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ContentPage extends QuartzEmitterPlugin {
 | 
					export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			||||||
| 
						 | 
					@ -26,40 +24,45 @@ export class ContentPage extends QuartzEmitterPlugin {
 | 
				
			||||||
    this.opts = opts
 | 
					    this.opts = opts
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getQuartzComponents(): QuartzComponent<any>[] {
 | 
				
			||||||
 | 
					    return [...Object.values(this.opts)]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
 | 
					  async emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emit: EmitCallback): Promise<string[]> {
 | 
				
			||||||
    const fps: string[] = []
 | 
					    const fps: string[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // emit styles
 | 
					    const { Head, Header } = this.opts
 | 
				
			||||||
    emit({
 | 
					 | 
				
			||||||
      slug: "index",
 | 
					 | 
				
			||||||
      ext: ".css",
 | 
					 | 
				
			||||||
      content: templateThemeStyles(cfg.theme, styles)
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    fps.push("index.css")
 | 
					 | 
				
			||||||
    resources.css.push(googleFontHref(cfg.theme))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (const [tree, file] of content) {
 | 
					    for (const [tree, file] of content) {
 | 
				
			||||||
      // @ts-ignore (preact makes it angry)
 | 
					      // @ts-ignore (preact makes it angry)
 | 
				
			||||||
      const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
					      const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: 'html' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const baseDir = resolveToRoot(file.data.slug!)
 | 
				
			||||||
 | 
					      const pageResources: StaticResources = {
 | 
				
			||||||
 | 
					        css: [baseDir + "/index.css", ...resources.css,],
 | 
				
			||||||
 | 
					        js: [
 | 
				
			||||||
 | 
					          { src: baseDir + "/prescript.js", loadTime: "beforeDOMReady", type: 'module' },
 | 
				
			||||||
 | 
					          ...resources.js,
 | 
				
			||||||
 | 
					          { src: baseDir + "/postscript.js", loadTime: "afterDOMReady", type: 'module' }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const title = file.data.frontmatter?.title
 | 
					      const title = file.data.frontmatter?.title
 | 
				
			||||||
      const { Head, Header } = this.opts
 | 
					 | 
				
			||||||
      const doc = <html>
 | 
					      const doc = <html>
 | 
				
			||||||
        <Head
 | 
					        <Head.Component
 | 
				
			||||||
          title={title ?? "Untitled"}
 | 
					          title={title ?? "Untitled"}
 | 
				
			||||||
          description={file.data.description ?? "No description provided"}
 | 
					          description={file.data.description ?? "No description provided"}
 | 
				
			||||||
          slug={file.data.slug!}
 | 
					          slug={file.data.slug!}
 | 
				
			||||||
          externalResources={resources} />
 | 
					          externalResources={pageResources} />
 | 
				
			||||||
        <body>
 | 
					        <body>
 | 
				
			||||||
          <div id="quartz-root" class="page">
 | 
					          <div id="quartz-root" class="page">
 | 
				
			||||||
            <Header title={cfg.siteTitle} slug={file.data.slug!} />
 | 
					            <Header.Component title={cfg.siteTitle} slug={file.data.slug!} />
 | 
				
			||||||
            <article>
 | 
					            <article>
 | 
				
			||||||
              {file.data.slug !== "index" && <h1>{title}</h1>}
 | 
					              {file.data.slug !== "index" && <h1>{title}</h1>}
 | 
				
			||||||
              {content}
 | 
					              {content}
 | 
				
			||||||
            </article>
 | 
					            </article>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </body>
 | 
					        </body>
 | 
				
			||||||
        {resources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} src={resource.src} />)}
 | 
					        {pageResources.js.filter(resource => resource.loadTime === "afterDOMReady").map(resource => <script key={resource.src} {...resource} />)}
 | 
				
			||||||
      </html>
 | 
					      </html>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const fp = file.data.slug + ".html"
 | 
					      const fp = file.data.slug + ".html"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,69 @@
 | 
				
			||||||
 | 
					import { GlobalConfiguration } from '../cfg'
 | 
				
			||||||
 | 
					import { QuartzComponent } from '../components/types'
 | 
				
			||||||
import { StaticResources } from '../resources'
 | 
					import { StaticResources } from '../resources'
 | 
				
			||||||
import { PluginTypes } from './types'
 | 
					import { googleFontHref, joinStyles } from '../theme'
 | 
				
			||||||
 | 
					import { EmitCallback, PluginTypes } from './types'
 | 
				
			||||||
 | 
					import styles from '../styles/base.scss'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ComponentResources = {
 | 
				
			||||||
 | 
					  css: string[],
 | 
				
			||||||
 | 
					  beforeDOMLoaded: string[],
 | 
				
			||||||
 | 
					  afterDOMLoaded: string[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function joinScripts(scripts: string[]): string {
 | 
				
			||||||
 | 
					  return scripts.join("\n")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function emitComponentResources(cfg: GlobalConfiguration, resources: StaticResources, plugins: PluginTypes, emit: EmitCallback) {
 | 
				
			||||||
 | 
					  const fps: string[] = []
 | 
				
			||||||
 | 
					  const allComponents: Set<QuartzComponent<any>> = new Set()
 | 
				
			||||||
 | 
					  for (const emitter of plugins.emitters) {
 | 
				
			||||||
 | 
					    const components = emitter.getQuartzComponents()
 | 
				
			||||||
 | 
					    for (const component of components) {
 | 
				
			||||||
 | 
					      allComponents.add(component)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const componentResources: ComponentResources = {
 | 
				
			||||||
 | 
					    css: [],
 | 
				
			||||||
 | 
					    beforeDOMLoaded: [],
 | 
				
			||||||
 | 
					    afterDOMLoaded: []
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const component of allComponents) {
 | 
				
			||||||
 | 
					    const { css, beforeDOMLoaded, afterDOMLoaded } = component
 | 
				
			||||||
 | 
					    if (css) {
 | 
				
			||||||
 | 
					      componentResources.css.push(css)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (beforeDOMLoaded) {
 | 
				
			||||||
 | 
					      componentResources.beforeDOMLoaded.push(beforeDOMLoaded)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (afterDOMLoaded) {
 | 
				
			||||||
 | 
					      componentResources.beforeDOMLoaded.push(afterDOMLoaded)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  emit({
 | 
				
			||||||
 | 
					    slug: "index",
 | 
				
			||||||
 | 
					    ext: ".css",
 | 
				
			||||||
 | 
					    content: joinStyles(cfg.theme, styles, ...componentResources.css)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  emit({
 | 
				
			||||||
 | 
					    slug: "prescript",
 | 
				
			||||||
 | 
					    ext: ".js",
 | 
				
			||||||
 | 
					    content: joinScripts(componentResources.beforeDOMLoaded)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  emit({
 | 
				
			||||||
 | 
					    slug: "postscript",
 | 
				
			||||||
 | 
					    ext: ".js",
 | 
				
			||||||
 | 
					    content: joinScripts(componentResources.afterDOMLoaded)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fps.push("index.css", "prescript.js", "postscript.js")
 | 
				
			||||||
 | 
					  resources.css.push(googleFontHref(cfg.theme))
 | 
				
			||||||
 | 
					  return fps
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
 | 
					export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
 | 
				
			||||||
  const staticResources: StaticResources = {
 | 
					  const staticResources: StaticResources = {
 | 
				
			||||||
| 
						 | 
					@ -7,8 +71,8 @@ export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
 | 
				
			||||||
    js: [],
 | 
					    js: [],
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (const plugin of plugins.transformers) {
 | 
					  for (const transformer of plugins.transformers) {
 | 
				
			||||||
    const res = plugin.externalResources
 | 
					    const res = transformer.externalResources
 | 
				
			||||||
    if (res?.js) {
 | 
					    if (res?.js) {
 | 
				
			||||||
      staticResources.js = staticResources.js.concat(res.js)
 | 
					      staticResources.js = staticResources.js.concat(res.js)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ export interface Options {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const defaultOptions: Options = {
 | 
					const defaultOptions: Options = {
 | 
				
			||||||
  highlight: true,
 | 
					  highlight: true,
 | 
				
			||||||
  wikilinks: true
 | 
					  wikilinks: true,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
 | 
					export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
 | 
				
			||||||
| 
						 | 
					@ -39,10 +39,10 @@ export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
 | 
				
			||||||
        return (tree: Root, _file) => {
 | 
					        return (tree: Root, _file) => {
 | 
				
			||||||
          findAndReplace(tree, backlinkRegex, (value: string, ...capture: string[]) => {
 | 
					          findAndReplace(tree, backlinkRegex, (value: string, ...capture: string[]) => {
 | 
				
			||||||
            if (value.startsWith("!")) {
 | 
					            if (value.startsWith("!")) {
 | 
				
			||||||
 | 
					              // TODO: handle embeds
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
              const [path, rawHeader, rawAlias] = capture
 | 
					              const [path, rawHeader, rawAlias] = capture
 | 
				
			||||||
              const anchor = rawHeader?.slice(1).trim() ?? ""
 | 
					              const anchor = rawHeader?.trim() ?? ""
 | 
				
			||||||
              const alias = rawAlias?.slice(1).trim() ?? path
 | 
					              const alias = rawAlias?.slice(1).trim() ?? path
 | 
				
			||||||
              const url = slugify(path.trim() + anchor)
 | 
					              const url = slugify(path.trim() + anchor)
 | 
				
			||||||
              return {
 | 
					              return {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ import { PluggableList } from "unified"
 | 
				
			||||||
import { StaticResources } from "../resources"
 | 
					import { StaticResources } from "../resources"
 | 
				
			||||||
import { ProcessedContent } from "./vfile"
 | 
					import { ProcessedContent } from "./vfile"
 | 
				
			||||||
import { GlobalConfiguration } from "../cfg"
 | 
					import { GlobalConfiguration } from "../cfg"
 | 
				
			||||||
 | 
					import { QuartzComponent } from "../components/types"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export abstract class QuartzTransformerPlugin {
 | 
					export abstract class QuartzTransformerPlugin {
 | 
				
			||||||
  abstract name: string
 | 
					  abstract name: string
 | 
				
			||||||
| 
						 | 
					@ -25,6 +26,7 @@ export type EmitCallback = (data: EmitOptions) => Promise<string>
 | 
				
			||||||
export abstract class QuartzEmitterPlugin {
 | 
					export abstract class QuartzEmitterPlugin {
 | 
				
			||||||
  abstract name: string
 | 
					  abstract name: string
 | 
				
			||||||
  abstract emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
 | 
					  abstract emit(cfg: GlobalConfiguration, content: ProcessedContent[], resources: StaticResources, emitCallback: EmitCallback): Promise<string[]>
 | 
				
			||||||
 | 
					  abstract getQuartzComponents(): QuartzComponent<any>[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface PluginTypes {
 | 
					export interface PluginTypes {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import path from "path"
 | 
				
			||||||
import fs from "fs"
 | 
					import fs from "fs"
 | 
				
			||||||
import { QuartzConfig } from "../cfg"
 | 
					import { QuartzConfig } from "../cfg"
 | 
				
			||||||
import { PerfTimer } from "../perf"
 | 
					import { PerfTimer } from "../perf"
 | 
				
			||||||
import { getStaticResourcesFromPlugins } from "../plugins"
 | 
					import { emitComponentResources, getStaticResourcesFromPlugins } from "../plugins"
 | 
				
			||||||
import { EmitCallback } from "../plugins/types"
 | 
					import { EmitCallback } from "../plugins/types"
 | 
				
			||||||
import { ProcessedContent } from "../plugins/vfile"
 | 
					import { ProcessedContent } from "../plugins/vfile"
 | 
				
			||||||
import { QUARTZ, slugify } from "../path"
 | 
					import { QUARTZ, slugify } from "../path"
 | 
				
			||||||
| 
						 | 
					@ -10,9 +10,6 @@ import { globbyStream } from "globby"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
 | 
					export async function emitContent(contentFolder: string, output: string, cfg: QuartzConfig, content: ProcessedContent[], verbose: boolean) {
 | 
				
			||||||
  const perf = new PerfTimer()
 | 
					  const perf = new PerfTimer()
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
 | 
					 | 
				
			||||||
  const emit: EmitCallback = async ({ slug, ext, content }) => {
 | 
					  const emit: EmitCallback = async ({ slug, ext, content }) => {
 | 
				
			||||||
    const pathToPage = path.join(output, slug + ext)
 | 
					    const pathToPage = path.join(output, slug + ext)
 | 
				
			||||||
    const dir = path.dirname(pathToPage)
 | 
					    const dir = path.dirname(pathToPage)
 | 
				
			||||||
| 
						 | 
					@ -21,6 +18,9 @@ export async function emitContent(contentFolder: string, output: string, cfg: Qu
 | 
				
			||||||
    return pathToPage
 | 
					    return pathToPage
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
 | 
				
			||||||
 | 
					  emitComponentResources(cfg.configuration, staticResources, cfg.plugins, emit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let emittedFiles = 0
 | 
					  let emittedFiles = 0
 | 
				
			||||||
  for (const emitter of cfg.plugins.emitters) {
 | 
					  for (const emitter of cfg.plugins.emitters) {
 | 
				
			||||||
    const emitted = await emitter.emit(cfg.configuration, content, staticResources, emit)
 | 
					    const emitted = await emitter.emit(cfg.configuration, content, staticResources, emit)
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,9 @@ export async function emitContent(contentFolder: string, output: string, cfg: Qu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const staticPath = path.join(QUARTZ, "static")
 | 
					  const staticPath = path.join(QUARTZ, "static")
 | 
				
			||||||
  await fs.promises.cp(staticPath, path.join(output, "static"), { recursive: true })
 | 
					  await fs.promises.cp(staticPath, path.join(output, "static"), { recursive: true })
 | 
				
			||||||
 | 
					  if (verbose) {
 | 
				
			||||||
 | 
					    console.log(`[emit:Static] ${path.join(output, "static", "**")}`)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // glob all non MD/MDX/HTML files in content folder and copy it over
 | 
					  // glob all non MD/MDX/HTML files in content folder and copy it over
 | 
				
			||||||
  const assetsPath = path.join("public", "assets")
 | 
					  const assetsPath = path.join("public", "assets")
 | 
				
			||||||
| 
						 | 
					@ -54,8 +57,5 @@ export async function emitContent(contentFolder: string, output: string, cfg: Qu
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (verbose) {
 | 
					  console.log(`Emitted ${emittedFiles} files to \`${output}\` in ${perf.timeSince()}`)
 | 
				
			||||||
    console.log(`[emit:Static] ${path.join(output, "static", "**")}`)
 | 
					 | 
				
			||||||
    console.log(`Emitted ${emittedFiles} files to \`${output}\` in ${perf.timeSince()}`)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,11 +6,18 @@ export function filterContent(plugins: QuartzFilterPlugin[], content: ProcessedC
 | 
				
			||||||
  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) {
 | 
				
			||||||
    content = content.filter(plugin.shouldPublish)
 | 
					    const updatedContent = content.filter(plugin.shouldPublish)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (verbose) {
 | 
				
			||||||
 | 
					      const diff = content.filter(x => !updatedContent.includes(x))
 | 
				
			||||||
 | 
					      for (const file of diff) {
 | 
				
			||||||
 | 
					        console.log(`[filter:${plugin.name}] ${file[1].data.slug}`)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    content = updatedContent
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (verbose) {
 | 
					  console.log(`Filtered out ${initialLength - content.length} files in ${perf.timeSince()}`)
 | 
				
			||||||
    console.log(`Filtered out ${initialLength - content.length} files in ${perf.timeSince()}`)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return content
 | 
					  return content
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,9 +50,6 @@ export async function parseMarkdown(processor: QuartzProcessor, baseDir: string,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (verbose) {
 | 
					  console.log(`Parsed and transformed ${res.length} Markdown files in ${perf.timeSince()}`)
 | 
				
			||||||
    console.log(`Parsed and transformed ${res.length} Markdown files in ${perf.timeSince()}`)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return res
 | 
					  return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
export interface JSResource {
 | 
					export interface JSResource {
 | 
				
			||||||
  src: string
 | 
					  src: string
 | 
				
			||||||
  loadTime: 'beforeDOMReady' | 'afterDOMReady'
 | 
					  loadTime: 'beforeDOMReady' | 'afterDOMReady'
 | 
				
			||||||
 | 
					  type?: 'module'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface StaticResources {
 | 
					export interface StaticResources {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,6 +98,8 @@ h1, h2, h3, h4, h5, h6 {
 | 
				
			||||||
    margin: 0 0.5rem;
 | 
					    margin: 0 0.5rem;
 | 
				
			||||||
    opacity: 0;
 | 
					    opacity: 0;
 | 
				
			||||||
    transition: opacity 0.2s ease;
 | 
					    transition: opacity 0.2s ease;
 | 
				
			||||||
 | 
					    transform: translateY(-0.1rem);
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
    font-family: var(--codeFont);
 | 
					    font-family: var(--codeFont);
 | 
				
			||||||
    user-select: none;
 | 
					    user-select: none;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,9 +26,8 @@ export function googleFontHref(theme: Theme) {
 | 
				
			||||||
  return `https://fonts.googleapis.com/css2?family=${code}&family=${header}:wght@400;700&family=${body}:ital,wght@0,400;0,600;1,400;1,600&display=swap`
 | 
					  return `https://fonts.googleapis.com/css2?family=${code}&family=${header}:wght@400;700&family=${body}:ital,wght@0,400;0,600;1,400;1,600&display=swap`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function templateThemeStyles(theme: Theme, stylesheet: string) {
 | 
					export function joinStyles(theme: Theme, ...stylesheet: string[]) {
 | 
				
			||||||
  return `
 | 
					  return `:root {
 | 
				
			||||||
:root {
 | 
					 | 
				
			||||||
  --light: ${theme.colors.lightMode.light};
 | 
					  --light: ${theme.colors.lightMode.light};
 | 
				
			||||||
  --lightgray: ${theme.colors.lightMode.lightgray};
 | 
					  --lightgray: ${theme.colors.lightMode.lightgray};
 | 
				
			||||||
  --gray: ${theme.colors.lightMode.gray};
 | 
					  --gray: ${theme.colors.lightMode.gray};
 | 
				
			||||||
| 
						 | 
					@ -54,6 +53,5 @@ export function templateThemeStyles(theme: Theme, stylesheet: string) {
 | 
				
			||||||
  --highlight: ${theme.colors.darkMode.highlight};
 | 
					  --highlight: ${theme.colors.darkMode.highlight};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
${stylesheet}
 | 
					${stylesheet.join("\n\n")}`
 | 
				
			||||||
`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue