https://quartz.jzhao.xyz/advanced/architecture

URL inviato:
https://quartz.jzhao.xyz/advanced/architecture
Report terminato:
LinkTesto
https://en.wikipedia.org/wiki/Shebang_(Unix)shebang
http://yargs.js.org/yargs
https://esbuild.github.io/esbuild
https://www.npmjs.com/package/esbuild-sass-pluginesbuild-sass-plugin v2
https://esbuild.github.io/api/#rebuildrebuild API
https://github.com/nodejs/modules/issues/307import cache
https://www.npmjs.com/package/workerpoolworkerpool
https://github.com/unifiedjs/unifiedunified
https://github.com/vfile/vfilevfile
https://www.npmjs.com/package/remark-parseremark-parse

Variabili JavaScript · 9 trovate

NomeTipo
onbeforetoggleobject
documentPictureInPictureobject
onscrollendobject
cfunction
ifunction
__cfBeaconobject
plausiblefunction
addCleanupfunction
spaNavigatefunction

Messaggi di log della console · 2 trovati

TipoCategoriaLog
infoother
URL
https://quartz.jzhao.xyz/postscript.js
Testo
WebGPU is experimental on this platform. See https://github.com/gpuweb/gpuweb/wiki/Implementation-Status#implementation-status
warningother
URL
https://quartz.jzhao.xyz/postscript.js
Testo
Failed to create WebGPU Context Provider

HTML

<!DOCTYPE html><html lang="en" saved-theme="light"><head><title>Architecture</title><meta charset="utf-8"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com"><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM Plex Mono&amp;family=Schibsted Grotesk:wght@400;700&amp;family=Source Sans Pro:ital,wght@0,400;0,600;1,400;1,600&amp;display=swap"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta property="og:title" content="Architecture"><meta property="og:description" content="Quartz is a static site generator. How does it work? This question is best answered by tracing what happens when a user (you!) runs npx quartz build in the command line: On the server After running npx quartz build, npm will look at package.json to find the bin entry for quartz which points at ./quartz/bootstrap-cli.mjs."><meta property="og:image" content="https://quartz.jzhao.xyz/static/og-image.png"><meta property="og:width" content="1200"><meta property="og:height" content="675"><link rel="icon" href="../static/icon.png"><meta name="description" content="Quartz is a static site generator. How does it work? This question is best answered by tracing what happens when a user (you!) runs npx quartz build in the command line: On the server After running npx quartz build, npm will look at package.json to find the bin entry for quartz which points at ./quartz/bootstrap-cli.mjs."><meta name="generator" content="Quartz"><link href="../index.css" rel="stylesheet" type="text/css" spa-preserve=""><link href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css" rel="stylesheet" type="text/css" spa-preserve=""><script src="../prescript.js" type="application/javascript" spa-preserve=""></script><script type="application/javascript" spa-preserve="">const fetchData = fetch("../static/contentIndex.json").then(data => data.json())</script><script src="https://plausible.io/js/script.manual.js" data-domain="quartz.jzhao.xyz" defer=""></script></head><body data-slug="advanced/architecture"><div id="quartz-root" class="page"><div id="quartz-body"><div class="left sidebar"><h2 class="page-title"><a href="..">🪴 Quartz 4.0</a></h2><div class="spacer mobile-only"></div><div class="search"><button class="search-button" id="search-button"><p>Search</p><svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title>Search</title><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"></path><circle cx="8" cy="8" r="7"></circle></g></svg></button><div id="search-container"><div id="search-space"><input autocomplete="off" id="search-bar" name="search" type="text" aria-label="Search for something" placeholder="Search for something"><div id="search-layout" data-preview="true"><div id="results-container"></div><div id="preview-container"></div></div></div></div></div><button class="darkmode" id="darkmode"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="dayIcon" x="0px" y="0px" viewBox="0 0 35 35" style="enable-background:new 0 0 35 35" xml:space="preserve" aria-label="Dark mode"><title>Dark mode</title><path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5    S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5    C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6    C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9    c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44    l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5    c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06    L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z     M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2    C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29    c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7    C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5    c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="nightIcon" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100" xml:space="preserve" aria-label="Light mode"><title>Light mode</title><path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571  C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23  c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369  c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65  c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z"></path></svg></button><div class="explorer desktop-only"><button type="button" id="explorer" data-behavior="collapse" data-collapsed="collapsed" data-savestate="true" data-tree="[{&quot;path&quot;:&quot;advanced&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;features&quot;,&quot;collapsed&quot;:true},{&quot;path&quot;:&quot;plugins&quot;,&quot;collapsed&quot;:true}]" aria-controls="explorer-content" aria-expanded="false"><h2>Explorer</h2><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="5 8 14 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="fold"><polyline points="6 9 12 15 18 9"></polyline></svg></button><div id="explorer-content"><ul class="overflow" id="explorer-ul"><li><div class="folder-outer open"><ul style="padding-left:0;" class="content" data-folderul=""><li><div class="folder-container"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="5 8 14 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="folder-icon"><polyline points="6 9 12 15 18 9"></polyline></svg><div data-folderpath="advanced"><button class="folder-button"><span class="folder-title">Advanced</span></button></div></div><div class="folder-outer"><ul style="padding-left:1.4rem;" class="content" data-folderul="advanced"><li><a href="../advanced/architecture" data-for="advanced/architecture">Architecture</a></li><li><a href="../advanced/creating-components" data-for="advanced/creating-components">Creating your own Quartz components</a></li><li><a href="../advanced/making-plugins" data-for="advanced/making-plugins">Making your own plugins</a></li><li><a href="../advanced/paths" data-for="advanced/paths">Paths in Quartz</a></li></ul></div></li><li><div class="folder-container"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="5 8 14 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="folder-icon"><polyline points="6 9 12 15 18 9"></polyline></svg><div data-folderpath="features"><button class="folder-button"><span class="folder-title">Feature List</span></button></div></div><div class="folder-outer"><ul style="padding-left:1.4rem;" class="content" data-folderul="features"><li><a href="../features/backlinks" data-for="features/backlinks">Backlinks</a></li><li><a href="../features/breadcrumbs" data-for="features/breadcrumbs">Breadcrumbs</a></li><li><a href="../features/callouts" data-for="features/callouts">Callouts</a></li><li><a href="../features/comments" data-for="features/comments">Comments</a></li><li><a href="../features/darkmode" data-for="features/darkmode">Darkmode</a></li><li><a href="../features/Docker-Support" data-for="features/Docker-Support">Docker Support</a></li><li><a href="../features/explorer" data-for="features/explorer">Explorer</a></li><li><a href="../features/folder-and-tag-listings" data-for="features/folder-and-tag-listings">Folder and Tag Listings</a></li><li><a href="../features/full-text-search" data-for="features/full-text-search">Full-text Search</a></li><li><a href="../features/graph-view" data-for="features/graph-view">Graph View</a></li><li><a href="../features/i18n" data-for="features/i18n">Internationalization</a></li><li><a href="../features/Latex" data-for="features/Latex">LaTeX</a></li><li><a href="../features/Mermaid-diagrams" data-for="features/Mermaid-diagrams">Mermaid Diagrams</a></li><li><a href="../features/Obsidian-compatibility" data-for="features/Obsidian-compatibility">Obsidian Compatibility</a></li><li><a href="../features/OxHugo-compatibility" data-for="features/OxHugo-compatibility">OxHugo Compatibility</a></li><li><a href="../features/popover-previews" data-for="features/popover-previews">Popover Previews</a></li><li><a href="../features/private-pages" data-for="features/private-pages">Private Pages</a></li><li><a href="../features/recent-notes" data-for="features/recent-notes">Recent Notes</a></li><li><a href="../features/Roam-Research-compatibility" data-for="features/Roam-Research-compatibility">Roam Research Compatibility</a></li><li><a href="../features/RSS-Feed" data-for="features/RSS-Feed">RSS Feed</a></li><li><a href="../features/SPA-Routing" data-for="features/SPA-Routing">SPA Routing</a></li><li><a href="../features/syntax-highlighting" data-for="features/syntax-highlighting">Syntax Highlighting</a></li><li><a href="../features/table-of-contents" data-for="features/table-of-contents">Table of Contents</a></li><li><a href="../features/wikilinks" data-for="features/wikilinks">Wikilinks</a></li></ul></div></li><li><div class="folder-container"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="5 8 14 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="folder-icon"><polyline points="6 9 12 15 18 9"></polyline></svg><div data-folderpath="plugins"><button class="folder-button"><span class="folder-title">Plugins</span></button></div></div><div class="folder-outer"><ul style="padding-left:1.4rem;" class="content" data-folderul="plugins"><li><a href="../plugins/AliasRedirects" data-for="plugins/AliasRedirects">AliasRedirects</a></li><li><a href="../plugins/Assets" data-for="plugins/Assets">Assets</a></li><li><a href="../plugins/CNAME" data-for="plugins/CNAME">CNAME</a></li><li><a href="../plugins/ComponentResources" data-for="plugins/ComponentResources">ComponentResources</a></li><li><a href="../plugins/ContentIndex" data-for="plugins/ContentIndex">ContentIndex</a></li><li><a href="../plugins/ContentPage" data-for="plugins/ContentPage">ContentPage</a></li><li><a href="../plugins/CrawlLinks" data-for="plugins/CrawlLinks">CrawlLinks</a></li><li><a href="../plugins/CreatedModifiedDate" data-for="plugins/CreatedModifiedDate">CreatedModifiedDate</a></li><li><a href="../plugins/Description" data-for="plugins/Description">Description</a></li><li><a href="../plugins/ExplicitPublish" data-for="plugins/ExplicitPublish">ExplicitPublish</a></li><li><a href="../plugins/FolderPage" data-for="plugins/FolderPage">FolderPage</a></li><li><a href="../plugins/Frontmatter" data-for="plugins/Frontmatter">Frontmatter</a></li><li><a href="../plugins/GitHubFlavoredMarkdown" data-for="plugins/GitHubFlavoredMarkdown">GitHubFlavoredMarkdown</a></li><li><a href="../plugins/HardLineBreaks" data-for="plugins/HardLineBreaks">HardLineBreaks</a></li><li><a href="../plugins/Latex" data-for="plugins/Latex">Latex</a></li><li><a href="../plugins/NotFoundPage" data-for="plugins/NotFoundPage">NotFoundPage</a></li><li><a href="../plugins/ObsidianFlavoredMarkdown" data-for="plugins/ObsidianFlavoredMarkdown">ObsidianFlavoredMarkdown</a></li><li><a href="../plugins/OxHugoFlavoredMarkdown" data-for="plugins/OxHugoFlavoredMarkdown">OxHugoFlavoredMarkdown</a></li><li><a href="../plugins/RemoveDrafts" data-for="plugins/RemoveDrafts">RemoveDrafts</a></li><li><a href="../plugins/RoamFlavoredMarkdown" data-for="plugins/RoamFlavoredMarkdown">RoamFlavoredMarkdown</a></li><li><a href="../plugins/Static" data-for="plugins/Static">Static</a></li><li><a href="../plugins/SyntaxHighlighting" data-for="plugins/SyntaxHighlighting">SyntaxHighlighting</a></li><li><a href="../plugins/TableOfContents" data-for="plugins/TableOfContents">TableOfContents</a></li><li><a href="../plugins/TagPage" data-for="plugins/TagPage">TagPage</a></li></ul></div></li><li><div class="folder-outer "><ul style="padding-left:0;" class="content" data-folderul=""></ul></div></li><li><a href="../authoring-content" data-for="authoring-content">Authoring Content</a></li><li><a href="../build" data-for="build">Building your Quartz</a></li><li><a href="../configuration" data-for="configuration">Configuration</a></li><li><a href="../hosting" data-for="hosting">Hosting</a></li><li><a href="../layout" data-for="layout">Layout</a></li><li><a href="../migrating-from-Quartz-3" data-for="migrating-from-Quartz-3">Migrating from Quartz 3</a></li><li><a href="../philosophy" data-for="philosophy">Philosophy of Quartz</a></li><li><a href="../showcase" data-for="showcase">Quartz Showcase</a></li><li><a href="../setting-up-your-GitHub-repository" data-for="setting-up-your-GitHub-repository">Setting up your GitHub repository</a></li><li><a href="../upgrading" data-for="upgrading">Upgrading Quartz</a></li></ul></div></li><li id="explorer-end"></li></ul></div></div></div><div class="center"><div class="page-header"><div class="popover-hint"><nav class="breadcrumb-container" aria-label="breadcrumbs"><div class="breadcrumb-element"><a href="../">Home</a><p> ❯ </p></div><div class="breadcrumb-element"><a href="../advanced/">Advanced</a><p> ❯ </p></div><div class="breadcrumb-element"><a href="">Architecture</a></div></nav><h1 class="article-title">Architecture</h1><p show-comma="true" class="content-meta"><span>Oct 18, 2024</span><span>6 min read</span></p></div></div><article class="popover-hint"><p>Quartz is a static site generator. How does it work?</p>
<p>This question is best answered by tracing what happens when a user (you!) runs <code>npx quartz build</code> in the command line:</p>
<h2 id="on-the-server">On the server<a role="anchor" aria-hidden="true" tabindex="-1" data-no-popover="true" href="#on-the-server" class="internal"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></a></h2>
<ol>
<li>After running <code>npx quartz build</code>, npm will look at <code>package.json</code> to find the <code>bin</code> entry for <code>quartz</code> which points at <code>./quartz/bootstrap-cli.mjs</code>.</li>
<li>This file has a <a href="https://en.wikipedia.org/wiki/Shebang_(Unix)" class="external">shebang<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> line at the top which tells npm to execute it using Node.</li>
<li><code>bootstrap-cli.mjs</code> is responsible for a few things:
<ol>
<li>Parsing the command-line arguments using <a href="http://yargs.js.org/" class="external">yargs<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a>.</li>
<li>Transpiling and bundling the rest of Quartz (which is in Typescript) to regular JavaScript using <a href="https://esbuild.github.io/" class="external">esbuild<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a>. The <code>esbuild</code> configuration here is slightly special as it also handles <code>.scss</code> file imports using <a href="https://www.npmjs.com/package/esbuild-sass-plugin" class="external">esbuild-sass-plugin v2<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a>. Additionally, we bundle ‘inline’ client-side scripts (any <code>.inline.ts</code> file) that components declare using a custom <code>esbuild</code> plugin that runs another instance of <code>esbuild</code> which bundles for the browser instead of <code>node</code>. Modules of both types are imported as plain text.</li>
<li>Running the local preview server if <code>--serve</code> is set. This starts two servers:
<ol>
<li>A WebSocket server on port 3001 to handle hot-reload signals. This tracks all inbound connections and sends a ‘rebuild’ message a server-side change is detected (either content or configuration).</li>
<li>An HTTP file-server on a user defined port (normally 8080) to serve the actual website files.</li>
</ol>
</li>
<li>If the <code>--serve</code> flag is set, it also starts a file watcher to detect source-code changes (e.g. anything that is <code>.ts</code>, <code>.tsx</code>, <code>.scss</code>, or packager files). On a change, we rebuild the module (step 2 above) using esbuild’s <a href="https://esbuild.github.io/api/#rebuild" class="external">rebuild API<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> which drastically reduces the build times.</li>
<li>After transpiling the main Quartz build module (<code>quartz/build.ts</code>), we write it to a cache file <code>.quartz-cache/transpiled-build.mjs</code> and then dynamically import this using <code>await import(cacheFile)</code>. However, we need to be pretty smart about how to bust Node’s <a href="https://github.com/nodejs/modules/issues/307" class="external">import cache<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> so we add a random query string to fake Node into thinking it’s a new module. This does, however, cause memory leaks so we just hope that the user doesn’t hot-reload their configuration too many times in a single session :)) (it leaks about ~350kB memory on each reload). After importing the module, we then invoke it, passing in the command line arguments we parsed earlier along with a callback function to signal the client to refresh.</li>
</ol>
</li>
<li>In <code>build.ts</code>, we start by installing source map support manually to account for the query string cache busting hack we introduced earlier. Then, we start processing content:
<ol>
<li>Clean the output directory.</li>
<li>Recursively glob all files in the <code>content</code> folder, respecting the <code>.gitignore</code>.</li>
<li>Parse the Markdown files.
<ol>
<li>Quartz detects the number of threads available and chooses to spawn worker threads if there are &gt;128 pieces of content to parse (rough heuristic). If it needs to spawn workers, it will invoke esbuild again to transpile the worker script <code>quartz/worker.ts</code>. Then, a work-stealing <a href="https://www.npmjs.com/package/workerpool" class="external">workerpool<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> is then created and batches of 128 files are assigned to workers.</li>
<li>Each worker (or just the main thread if there is no concurrency) creates a <a href="https://github.com/unifiedjs/unified" class="external">unified<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> parser based off of the plugins defined in the <a href="../configuration" class="internal" data-slug="configuration">configuration</a>.</li>
<li>Parsing has three steps:
<ol>
<li>Read the file into a <a href="https://github.com/vfile/vfile" class="external">vfile<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a>.</li>
<li>Applied plugin-defined text transformations over the content.</li>
<li>Slugify the file path and store it in the data for the file. See the page on <a href="../advanced/paths" class="internal" data-slug="advanced/paths">paths</a> for more details about how path logic works in Quartz (spoiler: its complicated).</li>
<li>Markdown parsing using <a href="https://www.npmjs.com/package/remark-parse" class="external">remark-parse<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> (text to <a href="https://github.com/syntax-tree/mdast" class="external">mdast<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a>).</li>
<li>Apply plugin-defined Markdown-to-Markdown transformations.</li>
<li>Convert Markdown into HTML using <a href="https://github.com/remarkjs/remark-rehype" class="external">remark-rehype<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> (<a href="https://github.com/syntax-tree/mdast" class="external">mdast<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> to <a href="https://github.com/syntax-tree/hast" class="external">hast<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a>).</li>
<li>Apply plugin-defined HTML-to-HTML transformations.</li>
</ol>
</li>
</ol>
</li>
<li>Filter out unwanted content using plugins.</li>
<li>Emit files using plugins.
<ol>
<li>Gather all the static resources (e.g. external CSS, JS modules, etc.) each emitter plugin declares.</li>
<li>Emitters that emit HTML files do a bit of extra work here as they need to transform the <a href="https://github.com/syntax-tree/hast" class="external">hast<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> produced in the parse step to JSX. This is done using <a href="https://github.com/syntax-tree/hast-util-to-jsx-runtime" class="external">hast-util-to-jsx-runtime<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> with the <a href="https://preactjs.com/" class="external">Preact<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> runtime. Finally, the JSX is rendered to HTML using <a href="https://github.com/preactjs/preact-render-to-string" class="external">preact-render-to-string<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> which statically renders the JSX to HTML (i.e. doesn’t care about <code>useState</code>, <code>useEffect</code>, or any other React/Preact interactive bits). Here, we also do a bunch of fun stuff like assemble the page <a href="../layout" class="internal" data-slug="layout">layout</a> from <code>quartz.layout.ts</code>, assemble all the inline scripts that actually get shipped to the client, and all the transpiled styles. The bulk of this logic can be found in <code>quartz/components/renderPage.tsx</code>. Other fun things of note:
<ol>
<li>CSS is minified and transformed using <a href="https://github.com/parcel-bundler/lightningcss" class="external">Lightning CSS<svg aria-hidden="true" class="external-icon" style="max-width:0.8em;max-height:0.8em;" viewBox="0 0 512 512"><path d="M320 0H288V64h32 82.7L201.4 265.4 178.7 288 224 333.3l22.6-22.6L448 109.3V192v32h64V192 32 0H480 320zM32 32H0V64 480v32H32 456h32V480 352 320H424v32 96H64V96h96 32V32H160 32z"></path></svg></a> to add vendor prefixes and do syntax lowering.</li>
<li>Scripts are split into <code>beforeDOMLoaded</code> and <code>afterDOMLoaded</code> and are inserted in the <code>&lt;head&gt;</code> and <code>&lt;body&gt;</code> respectively.</li>
</ol>
</li>
<li>Finally, each emitter plugin is responsible for emitting and writing it’s own emitted files to disk.</li>
</ol>
</li>
<li>If the <code>--serve</code> flag was detected, we also set up another file watcher to detect content changes (only <code>.md</code> files). We keep a content map that tracks the parsed AST and plugin data for each slug and update this on file changes. Newly added or modified paths are rebuilt and added to the content map. Then, all the filters and emitters are run over the resulting content map. This file watcher is debounced with a threshold of 250ms. On success, we send a client refresh signal using the passed in callback function.</li>
</ol>
</li>
</ol>
<h2 id="on-the-client">On the client<a role="anchor" aria-hidden="true" tabindex="-1" data-no-popover="true" href="#on-the-client" class="internal"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></a></h2>
<ol>
<li>The browser opens a Quartz page and loads the HTML. The <code>&lt;head&gt;</code> also links to page styles (emitted to <code>public/index.css</code>) and page-critical JS (emitted to <code>public/prescript.js</code>)</li>
<li>Then, once the body is loaded, the browser loads the non-critical JS (emitted to <code>public/postscript.js</code>)</li>
<li>Once the page is done loading, the page will then dispatch a custom synthetic browser event <code>"nav"</code>. This is used so client-side scripts declared by components can ‘setup’ anything that requires access to the page DOM.
<ol>
<li>If the <a href="../features/SPA-Routing" class="internal alias" data-slug="features/SPA-Routing">enableSPA option</a> is enabled in the <a href="../configuration" class="internal" data-slug="configuration">configuration</a>, this <code>"nav"</code> event is also fired on any client-navigation to allow for components to unregister and reregister any event handlers and state.</li>
<li>If it’s not, we wire up the <code>"nav"</code> event to just be fired a single time after page load to allow for consistency across how state is setup across both SPA and non-SPA contexts.</li>
</ol>
</li>
</ol>
<p>The architecture and design of the plugin system was intentionally left pretty vague here as this is described in much more depth in the guide on <a href="../advanced/making-plugins" class="internal alias" data-slug="advanced/making-plugins">making your own plugin</a>.</p></article><hr><div class="page-footer"></div></div><div class="right sidebar"><div class="graph"><h3>Graph View</h3><div class="graph-outer"><div id="graph-container" data-cfg="{&quot;drag&quot;:true,&quot;zoom&quot;:true,&quot;depth&quot;:1,&quot;scale&quot;:1.1,&quot;repelForce&quot;:0.5,&quot;centerForce&quot;:0.3,&quot;linkDistance&quot;:30,&quot;fontSize&quot;:0.6,&quot;opacityScale&quot;:1,&quot;showTags&quot;:true,&quot;removeTags&quot;:[],&quot;focusOnHover&quot;:false}"></div><button id="global-graph-icon" aria-label="Global Graph"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 55 55" fill="currentColor" xml:space="preserve"><path d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
                s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
                c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562
                C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829
                c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91
                v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4
                s-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665
                C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2
                S11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4
                s1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2
                s-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"></path></svg></button></div><div id="global-graph-outer"><div id="global-graph-container" data-cfg="{&quot;drag&quot;:true,&quot;zoom&quot;:true,&quot;depth&quot;:-1,&quot;scale&quot;:0.9,&quot;repelForce&quot;:0.5,&quot;centerForce&quot;:0.3,&quot;linkDistance&quot;:30,&quot;fontSize&quot;:0.6,&quot;opacityScale&quot;:1,&quot;showTags&quot;:true,&quot;removeTags&quot;:[],&quot;focusOnHover&quot;:true}"></div></div></div><div class="toc desktop-only"><button type="button" id="toc" class="" aria-controls="toc-content" aria-expanded="true"><h3>Table of Contents</h3><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="fold"><polyline points="6 9 12 15 18 9"></polyline></svg></button><div id="toc-content" class=""><ul class="overflow"><li class="depth-0"><a href="#on-the-server" data-for="on-the-server" class="in-view">On the server</a></li><li class="depth-0"><a href="#on-the-client" data-for="on-the-client">On the client</a></li></ul></div></div><div class="backlinks"><h3>Backlinks</h3><ul class="overflow"><li><a href="../" class="internal">Welcome to Quartz 4</a></li></ul></div></div><footer class=""><p>Created with <a href="https://quartz.jzhao.xyz/">Quartz v4.4.0</a> © 2024</p><ul><li><a href="https://github.com/jackyzha0/quartz">GitHub</a></li><li><a href="https://discord.gg/cRFFHYye7t">Discord Community</a></li></ul></footer></div></div><!-- Cloudflare Pages Analytics --><script defer="" src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon="{&quot;token&quot;: &quot;44ce89144864400b9049f925b22f9a66&quot;}"></script><!-- Cloudflare Pages Analytics --><script defer="" src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon="{&quot;rayId&quot;:&quot;8d5f8a8c0e9a6677&quot;,&quot;version&quot;:&quot;2024.10.1&quot;,&quot;r&quot;:1,&quot;serverTiming&quot;:{&quot;name&quot;:{&quot;cfExtPri&quot;:true,&quot;cfL4&quot;:true,&quot;cfSpeedBrain&quot;:true,&quot;cfCacheStatus&quot;:true}},&quot;token&quot;:&quot;26aabd7ec7c54f6ca5b0744dfa6eb78a&quot;,&quot;b&quot;:1}" crossorigin="anonymous"></script>
<script type="application/javascript">function c(){let t=this.parentElement;t.classList.toggle("is-collapsed");let l=t.classList.contains("is-collapsed")?this.scrollHeight:t.scrollHeight;t.style.maxHeight=l+"px";let o=t,e=t.parentElement;for(;e;){if(!e.classList.contains("callout"))return;let n=e.classList.contains("is-collapsed")?e.scrollHeight:e.scrollHeight+o.scrollHeight;e.style.maxHeight=n+"px",o=e,e=e.parentElement}}function i(){let t=document.getElementsByClassName("callout is-collapsible");for(let s of t){let l=s.firstElementChild;if(l){l.addEventListener("click",c),window.addCleanup(()=>l.removeEventListener("click",c));let e=s.classList.contains("is-collapsed")?l.scrollHeight:s.scrollHeight;s.style.maxHeight=e+"px"}}}document.addEventListener("nav",i);window.addEventListener("resize",i);
</script><script type="module">
          let mermaidImport = undefined
          document.addEventListener('nav', async () => {
            if (document.querySelector("code.mermaid")) {
              mermaidImport ||= await import('https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.7.0/mermaid.esm.min.mjs')
              const mermaid = mermaidImport.default
              const darkMode = document.documentElement.getAttribute('saved-theme') === 'dark'
              mermaid.initialize({
                startOnLoad: false,
                securityLevel: 'loose',
                theme: darkMode ? 'dark' : 'default'
              })

              await mermaid.run({
                querySelector: '.mermaid'
              })
            }
          });
          </script><script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/copy-tex.min.js" type="application/javascript"></script><script src="../postscript.js" type="module"></script></body></html>