diff --git a/docs/build.md b/docs/build.md index 1447687..6005770 100644 --- a/docs/build.md +++ b/docs/build.md @@ -21,3 +21,7 @@ This will start a local web server to run your Quartz on your computer. Open a w > - `--serve`: run a local hot-reloading server to preview your Quartz > - `--port`: what port to run the local preview server on > - `--concurrency`: how many threads to use to parse notes + +> [!warning] Not to be used for production +> Serve mode is intended for local previews only. +> For production workloads, see the page on [[hosting]]. diff --git a/package-lock.json b/package-lock.json index c2a746f..c80a85f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,7 +83,7 @@ "esbuild": "^0.19.9", "prettier": "^3.3.3", "tsx": "^4.19.0", - "typescript": "^5.5.4" + "typescript": "^5.6.2" }, "engines": { "node": "20 || >=22", @@ -4790,9 +4790,9 @@ } }, "node_modules/preact-render-to-string": { - "version": "6.5.10", - "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.10.tgz", - "integrity": "sha512-BJdypTQaBA5UbTF9NKZS3zP93Sw33tZOxNXIfuHofqOZFoMdsquNkVebs/HkEw0in/Qbi6Ep/Anngnj+VsHeBQ==", + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz", + "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==", "peerDependencies": { "preact": ">=10" } @@ -6261,9 +6261,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6475,12 +6475,11 @@ } }, "node_modules/vfile": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.2.tgz", - "integrity": "sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "dependencies": { "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" }, "funding": { diff --git a/package.json b/package.json index 26921d6..c71d1dd 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,6 @@ "esbuild": "^0.19.9", "prettier": "^3.3.3", "tsx": "^4.19.0", - "typescript": "^5.5.4" + "typescript": "^5.6.2" } } diff --git a/quartz/build.ts b/quartz/build.ts index 342a27c..67ec0da 100644 --- a/quartz/build.ts +++ b/quartz/build.ts @@ -39,7 +39,7 @@ type BuildData = { type FileEvent = "add" | "change" | "delete" function newBuildId() { - return new Date().toISOString() + return Math.random().toString(36).substring(2, 8) } async function buildQuartz(argv: Argv, mut: Mutex, clientRefresh: () => void) { @@ -162,17 +162,19 @@ async function partialRebuildFromEntrypoint( return } - const buildStart = new Date().getTime() - buildData.lastBuildMs = buildStart + const buildId = newBuildId() + ctx.buildId = buildId + buildData.lastBuildMs = new Date().getTime() const release = await mut.acquire() - if (buildData.lastBuildMs > buildStart) { + + // if there's another build after us, release and let them do it + if (ctx.buildId !== buildId) { release() return } const perf = new PerfTimer() console.log(chalk.yellow("Detected change, rebuilding...")) - ctx.buildId = newBuildId() // UPDATE DEP GRAPH const fp = joinSegments(argv.directory, toPosixPath(filepath)) as FilePath @@ -357,19 +359,19 @@ async function rebuildFromEntrypoint( toRemove.add(filePath) } - const buildStart = new Date().getTime() - buildData.lastBuildMs = buildStart + const buildId = newBuildId() + ctx.buildId = buildId + buildData.lastBuildMs = new Date().getTime() const release = await mut.acquire() // there's another build after us, release and let them do it - if (buildData.lastBuildMs > buildStart) { + if (ctx.buildId !== buildId) { release() return } const perf = new PerfTimer() console.log(chalk.yellow("Detected change, rebuilding...")) - ctx.buildId = newBuildId() try { const filesToRebuild = [...toRebuild].filter((fp) => !toRemove.has(fp)) @@ -405,10 +407,10 @@ async function rebuildFromEntrypoint( } } - release() clientRefresh() toRebuild.clear() toRemove.clear() + release() } export default async (argv: Argv, mut: Mutex, clientRefresh: () => void) => { diff --git a/quartz/components/Explorer.tsx b/quartz/components/Explorer.tsx index ec7c48e..df876ab 100644 --- a/quartz/components/Explorer.tsx +++ b/quartz/components/Explorer.tsx @@ -7,6 +7,7 @@ import { ExplorerNode, FileNode, Options } from "./ExplorerNode" import { QuartzPluginData } from "../plugins/vfile" import { classNames } from "../util/lang" import { i18n } from "../i18n" +import { VNode } from "preact" // Options interface defined in `ExplorerNode` to avoid circular dependency const defaultOptions = { @@ -44,6 +45,7 @@ export default ((userOpts?: Partial) => { // memoized let fileTree: FileNode let jsonTree: string + let component: VNode let lastBuildId: string = "" function constructFileTree(allFiles: QuartzPluginData[]) { @@ -82,44 +84,46 @@ export default ((userOpts?: Partial) => { if (ctx.buildId !== lastBuildId) { lastBuildId = ctx.buildId constructFileTree(allFiles) + const tree = ExplorerNode({ node: fileTree, opts, fileData }) + component = ( +
+ +
+
    + {tree} +
  • +
+
+
+ ) } - return ( -
- -
-
    - -
  • -
-
-
- ) + return component } Explorer.css = explorerStyle diff --git a/quartz/components/ExplorerNode.tsx b/quartz/components/ExplorerNode.tsx index e57d677..3137a3f 100644 --- a/quartz/components/ExplorerNode.tsx +++ b/quartz/components/ExplorerNode.tsx @@ -224,15 +224,10 @@ export function ExplorerNode({ node, opts, fullPath, fileData }: ExplorerNodePro class="content" data-folderul={folderPath} > - {node.children.map((childNode, i) => ( - - ))} + {node.children.map((childNode) => + // eagerly render children so we can memoize properly + ExplorerNode({ node: childNode, opts, fileData, fullPath: folderPath }), + )} diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts index 38bc0dc..3e8dbde 100644 --- a/quartz/plugins/transformers/links.ts +++ b/quartz/plugins/transformers/links.ts @@ -67,6 +67,7 @@ export const CrawlLinks: QuartzTransformerPlugin> = (userOpts) properties: { "aria-hidden": "true", class: "external-icon", + style: "max-width:0.8em;max-height:0.8em", viewBox: "0 0 512 512", }, children: [ diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index 0dea893..dd743b6 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -324,8 +324,8 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> replacements.push([ tagRegex, (_value: string, tag: string) => { - // Check if the tag only includes numbers - if (/^\d+$/.test(tag)) { + // Check if the tag only includes numbers and slashes + if (/^[\/\d]+$/.test(tag)) { return false }