deps: bump everything (closes #635) (#636)
* deps: bump ws
* deps: bump lightningcss
* deps: workerpool
* deps: various types
* deps: chalk
* deps: globby
* deps: preact
* deps: tsx
* deps: @floating-ui/dom
* deps: esbuild
* deps: types + prettier
* deps: rimraf, typescript
* deps: remark/rehype/unified ecosystem
* format
| | |
| | | "quartz": "./quartz/bootstrap-cli.mjs" |
| | | }, |
| | | "dependencies": { |
| | | "@clack/prompts": "^0.6.3", |
| | | "@floating-ui/dom": "^1.4.0", |
| | | "@clack/prompts": "^0.7.0", |
| | | "@floating-ui/dom": "^1.5.3", |
| | | "@napi-rs/simple-git": "0.1.9", |
| | | "async-mutex": "^0.4.0", |
| | | "chalk": "^4.1.2", |
| | | "chalk": "^5.3.0", |
| | | "chokidar": "^3.5.3", |
| | | "cli-spinner": "^0.2.10", |
| | | "d3": "^7.8.5", |
| | | "esbuild-sass-plugin": "^2.12.0", |
| | | "esbuild-sass-plugin": "^2.16.0", |
| | | "flexsearch": "0.7.21", |
| | | "github-slugger": "^2.0.0", |
| | | "globby": "^13.1.4", |
| | | "globby": "^14.0.0", |
| | | "gray-matter": "^4.0.3", |
| | | "hast-util-to-html": "^8.0.4", |
| | | "hast-util-to-jsx-runtime": "^1.2.0", |
| | | "hast-util-to-string": "^2.0.0", |
| | | "hast-util-to-html": "^9.0.0", |
| | | "hast-util-to-jsx-runtime": "^2.3.0", |
| | | "hast-util-to-string": "^3.0.0", |
| | | "is-absolute-url": "^4.0.1", |
| | | "js-yaml": "^4.1.0", |
| | | "lightningcss": "1.21.7", |
| | | "mdast-util-find-and-replace": "^2.2.2", |
| | | "mdast-util-to-hast": "^12.3.0", |
| | | "mdast-util-to-string": "^3.2.0", |
| | | "lightningcss": "^1.22.1", |
| | | "mdast-util-find-and-replace": "^3.0.1", |
| | | "mdast-util-to-hast": "^13.0.2", |
| | | "mdast-util-to-string": "^4.0.0", |
| | | "micromorph": "^0.4.5", |
| | | "plausible-tracker": "^0.3.8", |
| | | "preact": "^10.14.1", |
| | | "preact-render-to-string": "^6.0.3", |
| | | "pretty-bytes": "^6.1.0", |
| | | "preact": "^10.19.3", |
| | | "preact-render-to-string": "^6.3.1", |
| | | "pretty-bytes": "^6.1.1", |
| | | "pretty-time": "^1.1.0", |
| | | "reading-time": "^1.5.0", |
| | | "rehype-autolink-headings": "^6.1.1", |
| | | "rehype-katex": "^6.0.3", |
| | | "rehype-mathjax": "^4.0.3", |
| | | "rehype-pretty-code": "^0.10.0", |
| | | "rehype-raw": "^6.1.1", |
| | | "rehype-slug": "^5.1.0", |
| | | "remark": "^14.0.2", |
| | | "remark-breaks": "^3.0.3", |
| | | "remark-frontmatter": "^4.0.1", |
| | | "remark-gfm": "^3.0.1", |
| | | "remark-math": "^5.1.1", |
| | | "remark-parse": "^10.0.1", |
| | | "remark-rehype": "^10.1.0", |
| | | "rehype-autolink-headings": "^7.1.0", |
| | | "rehype-katex": "^7.0.0", |
| | | "rehype-mathjax": "^5.0.0", |
| | | "rehype-pretty-code": "^0.12.1", |
| | | "rehype-raw": "^7.0.0", |
| | | "rehype-slug": "^6.0.0", |
| | | "remark": "^15.0.1", |
| | | "remark-breaks": "^4.0.0", |
| | | "remark-frontmatter": "^5.0.0", |
| | | "remark-gfm": "^4.0.0", |
| | | "remark-math": "^6.0.0", |
| | | "remark-parse": "^11.0.0", |
| | | "remark-rehype": "^11.0.0", |
| | | "remark-smartypants": "^2.0.0", |
| | | "rimraf": "^5.0.1", |
| | | "rimraf": "^5.0.5", |
| | | "serve-handler": "^6.1.5", |
| | | "shikiji": "^0.8.7", |
| | | "source-map-support": "^0.5.21", |
| | | "to-vfile": "^7.2.4", |
| | | "to-vfile": "^8.0.0", |
| | | "toml": "^3.0.0", |
| | | "unified": "^10.1.2", |
| | | "unist-util-visit": "^4.1.2", |
| | | "vfile": "^5.3.7", |
| | | "workerpool": "^6.4.0", |
| | | "ws": "^8.13.0", |
| | | "unified": "^11.0.4", |
| | | "unist-util-visit": "^5.0.0", |
| | | "vfile": "^6.0.1", |
| | | "workerpool": "^8.0.0", |
| | | "ws": "^8.15.1", |
| | | "yargs": "^17.7.2" |
| | | }, |
| | | "devDependencies": { |
| | | "@types/cli-spinner": "^0.2.1", |
| | | "@types/d3": "^7.4.0", |
| | | "@types/cli-spinner": "^0.2.3", |
| | | "@types/d3": "^7.4.3", |
| | | "@types/flexsearch": "^0.7.3", |
| | | "@types/hast": "^2.3.4", |
| | | "@types/js-yaml": "^4.0.5", |
| | | "@types/hast": "^3.0.3", |
| | | "@types/js-yaml": "^4.0.9", |
| | | "@types/node": "^20.1.2", |
| | | "@types/pretty-time": "^1.1.2", |
| | | "@types/source-map-support": "^0.5.6", |
| | | "@types/workerpool": "^6.4.0", |
| | | "@types/ws": "^8.5.5", |
| | | "@types/yargs": "^17.0.24", |
| | | "esbuild": "0.19.2", |
| | | "prettier": "^3.0.0", |
| | | "tsx": "^3.12.7", |
| | | "typescript": "^5.0.4" |
| | | "@types/pretty-time": "^1.1.5", |
| | | "@types/source-map-support": "^0.5.10", |
| | | "@types/workerpool": "^6.4.7", |
| | | "@types/ws": "^8.5.10", |
| | | "@types/yargs": "^17.0.32", |
| | | "esbuild": "^0.19.9", |
| | | "prettier": "^3.1.1", |
| | | "tsx": "^4.6.2", |
| | | "typescript": "^5.3.3" |
| | | } |
| | | } |
| | |
| | | const classNames = (node.properties?.className ?? []) as string[] |
| | | if (classNames.includes("transclude")) { |
| | | const inner = node.children[0] as Element |
| | | const transcludeTarget = inner.properties?.["data-slug"] as FullSlug |
| | | const transcludeTarget = inner.properties["data-slug"] as FullSlug |
| | | const page = getOrComputeFileIndex(componentData.allFiles).get(transcludeTarget) |
| | | if (!page) { |
| | | return |
| | | } |
| | | |
| | | let blockRef = node.properties?.dataBlock as string | undefined |
| | | let blockRef = node.properties.dataBlock as string | undefined |
| | | if (blockRef?.startsWith("#^")) { |
| | | // block transclude |
| | | blockRef = blockRef.slice("#^".length) |
| | |
| | | blockNode = { |
| | | type: "element", |
| | | tagName: "ul", |
| | | properties: {}, |
| | | children: [blockNode], |
| | | } |
| | | } |
| | |
| | | { |
| | | type: "element", |
| | | tagName: "h1", |
| | | properties: {}, |
| | | children: [ |
| | | { type: "text", value: page.frontmatter?.title ?? `Transclude of ${page.slug}` }, |
| | | ], |
| | |
| | | float: right; |
| | | right: 0; |
| | | padding: 0.4rem; |
| | | margin: -0.2rem 0.3rem; |
| | | margin: 0.3rem; |
| | | color: var(--gray); |
| | | border-color: var(--dark); |
| | | background-color: var(--light); |
| | |
| | | fileData: QuartzPluginData |
| | | cfg: GlobalConfiguration |
| | | children: (QuartzComponent | JSX.Element)[] |
| | | tree: Node<QuartzPluginData> |
| | | tree: Node |
| | | allFiles: QuartzPluginData[] |
| | | displayClass?: "mobile-only" | "desktop-only" |
| | | } & JSX.IntrinsicAttributes & { |
| | |
| | | [remarkFrontmatter, ["yaml", "toml"]], |
| | | () => { |
| | | return (_, file) => { |
| | | const { data } = matter(file.value, { |
| | | const { data } = matter(Buffer.from(file.value), { |
| | | ...opts, |
| | | engines: { |
| | | yaml: (s) => yaml.load(s, { schema: yaml.JSON_SCHEMA }) as object, |
| | |
| | | } |
| | | |
| | | // slug them all!! |
| | | data.tags = [...new Set(data.tags?.map((tag: string) => slugTag(tag)))] ?? [] |
| | | data.tags = [...new Set(data.tags?.map((tag: string) => slugTag(tag)))] |
| | | |
| | | // fill in frontmatter |
| | | file.data.frontmatter = data as QuartzPluginData["frontmatter"] |
| | |
| | | import remarkMath from "remark-math" |
| | | import rehypeKatex from "rehype-katex" |
| | | import rehypeMathjax from "rehype-mathjax/svg.js" |
| | | import rehypeMathjax from "rehype-mathjax/svg" |
| | | import { QuartzTransformerPlugin } from "../types" |
| | | |
| | | interface Options { |
| | |
| | | import { PluggableList } from "unified" |
| | | import { QuartzTransformerPlugin } from "../types" |
| | | import { Root, HTML, BlockContent, DefinitionContent, Code, Paragraph } from "mdast" |
| | | import { Root, Html, BlockContent, DefinitionContent, Code, Paragraph } from "mdast" |
| | | import { Element, Literal, Root as HtmlRoot } from "hast" |
| | | import { Replace, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace" |
| | | import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace" |
| | | import { slug as slugAnchor } from "github-slugger" |
| | | import rehypeRaw from "rehype-raw" |
| | | import { visit } from "unist-util-visit" |
| | |
| | | import { toHtml } from "hast-util-to-html" |
| | | import { PhrasingContent } from "mdast-util-find-and-replace/lib" |
| | | import { capitalize } from "../../util/lang" |
| | | import { PluggableList } from "unified" |
| | | |
| | | export interface Options { |
| | | comments: boolean |
| | |
| | | return toHtml(hast, { allowDangerousHtml: true }) |
| | | } |
| | | |
| | | const findAndReplace = opts.enableInHtmlEmbed |
| | | ? (tree: Root, regex: RegExp, replace?: Replace | null | undefined) => { |
| | | if (replace) { |
| | | visit(tree, "html", (node: HTML) => { |
| | | if (typeof replace === "string") { |
| | | node.value = node.value.replace(regex, replace) |
| | | } else { |
| | | node.value = node.value.replaceAll(regex, (substring: string, ...args) => { |
| | | const replaceValue = replace(substring, ...args) |
| | | if (typeof replaceValue === "string") { |
| | | return replaceValue |
| | | } else if (Array.isArray(replaceValue)) { |
| | | return replaceValue.map(mdastToHtml).join("") |
| | | } else if (typeof replaceValue === "object" && replaceValue !== null) { |
| | | return mdastToHtml(replaceValue) |
| | | } else { |
| | | return substring |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | mdastFindReplace(tree, regex, replace) |
| | | } |
| | | : mdastFindReplace |
| | | |
| | | return { |
| | | name: "ObsidianFlavoredMarkdown", |
| | | textTransform(_ctx, src) { |
| | | // pre-transform blockquotes |
| | | if (opts.callouts) { |
| | | src = src.toString() |
| | | if (src instanceof Buffer) { |
| | | src = src.toString() |
| | | } |
| | | |
| | | src = src.replaceAll(calloutLineRegex, (value) => { |
| | | // force newline after title of callout |
| | | return value + "\n> " |
| | |
| | | |
| | | // pre-transform wikilinks (fix anchors to things that may contain illegal syntax e.g. codeblocks, latex) |
| | | if (opts.wikilinks) { |
| | | src = src.toString() |
| | | if (src instanceof Buffer) { |
| | | src = src.toString() |
| | | } |
| | | |
| | | src = src.replaceAll(wikilinkRegex, (value, ...capture) => { |
| | | const [rawFp, rawHeader, rawAlias] = capture |
| | | const fp = rawFp ?? "" |
| | |
| | | }, |
| | | markdownPlugins() { |
| | | const plugins: PluggableList = [] |
| | | if (opts.wikilinks) { |
| | | plugins.push(() => { |
| | | return (tree: Root, _file) => { |
| | | findAndReplace(tree, wikilinkRegex, (value: string, ...capture: string[]) => { |
| | | let [rawFp, rawHeader, rawAlias] = capture |
| | | const fp = rawFp?.trim() ?? "" |
| | | const anchor = rawHeader?.trim() ?? "" |
| | | const alias = rawAlias?.slice(1).trim() |
| | | |
| | | // embed cases |
| | | if (value.startsWith("!")) { |
| | | const ext: string = path.extname(fp).toLowerCase() |
| | | const url = slugifyFilePath(fp as FilePath) |
| | | 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, |
| | | // regex replacements |
| | | plugins.push(() => { |
| | | return (tree: Root, file) => { |
| | | const replacements: [RegExp, string | ReplaceFunction][] = [] |
| | | const base = pathToRoot(file.data.slug!) |
| | | |
| | | if (opts.wikilinks) { |
| | | replacements.push([ |
| | | wikilinkRegex, |
| | | (value: string, ...capture: string[]) => { |
| | | let [rawFp, rawHeader, rawAlias] = capture |
| | | const fp = rawFp?.trim() ?? "" |
| | | const anchor = rawHeader?.trim() ?? "" |
| | | const alias = rawAlias?.slice(1).trim() |
| | | |
| | | // embed cases |
| | | if (value.startsWith("!")) { |
| | | const ext: string = path.extname(fp).toLowerCase() |
| | | const url = slugifyFilePath(fp as FilePath) |
| | | 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 (ext === "") { |
| | | const block = anchor |
| | | return { |
| | | type: "html", |
| | | data: { hProperties: { transclude: true } }, |
| | | value: `<blockquote class="transclude" data-url="${url}" data-block="${block}"><a href="${ |
| | | url + anchor |
| | | }" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`, |
| | | } |
| | | } |
| | | } 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 (ext === "") { |
| | | const block = anchor |
| | | return { |
| | | type: "html", |
| | | data: { hProperties: { transclude: true } }, |
| | | value: `<blockquote class="transclude" data-url="${url}" data-block="${block}"><a href="${ |
| | | url + anchor |
| | | }" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`, |
| | | } |
| | | |
| | | // otherwise, fall through to regular link |
| | | } |
| | | |
| | | // otherwise, fall through to regular link |
| | | } |
| | | // internal link |
| | | const url = fp + anchor |
| | | return { |
| | | type: "link", |
| | | url, |
| | | children: [ |
| | | { |
| | | type: "text", |
| | | value: alias ?? fp, |
| | | }, |
| | | ], |
| | | } |
| | | }, |
| | | ]) |
| | | } |
| | | |
| | | // internal link |
| | | const url = fp + anchor |
| | | return { |
| | | type: "link", |
| | | url, |
| | | children: [ |
| | | { |
| | | type: "text", |
| | | value: alias ?? fp, |
| | | if (opts.highlight) { |
| | | replacements.push([ |
| | | highlightRegex, |
| | | (_value: string, ...capture: string[]) => { |
| | | const [inner] = capture |
| | | return { |
| | | type: "html", |
| | | value: `<span class="text-highlight">${inner}</span>`, |
| | | } |
| | | }, |
| | | ]) |
| | | } |
| | | |
| | | if (opts.comments) { |
| | | replacements.push([ |
| | | commentRegex, |
| | | (_value: string, ..._capture: string[]) => { |
| | | return { |
| | | type: "text", |
| | | value: "", |
| | | } |
| | | }, |
| | | ]) |
| | | } |
| | | |
| | | if (opts.parseTags) { |
| | | replacements.push([ |
| | | tagRegex, |
| | | (_value: string, tag: string) => { |
| | | // Check if the tag only includes numbers |
| | | if (/^\d+$/.test(tag)) { |
| | | return false |
| | | } |
| | | |
| | | tag = slugTag(tag) |
| | | if (file.data.frontmatter && !file.data.frontmatter.tags.includes(tag)) { |
| | | file.data.frontmatter.tags.push(tag) |
| | | } |
| | | |
| | | return { |
| | | type: "link", |
| | | url: base + `/tags/${tag}`, |
| | | data: { |
| | | hProperties: { |
| | | className: ["tag-link"], |
| | | }, |
| | | }, |
| | | ], |
| | | } |
| | | }) |
| | | children: [ |
| | | { |
| | | type: "text", |
| | | value: `#${tag}`, |
| | | }, |
| | | ], |
| | | } |
| | | }, |
| | | ]) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | if (opts.highlight) { |
| | | plugins.push(() => { |
| | | return (tree: Root, _file) => { |
| | | findAndReplace(tree, highlightRegex, (_value: string, ...capture: string[]) => { |
| | | const [inner] = capture |
| | | return { |
| | | type: "html", |
| | | value: `<span class="text-highlight">${inner}</span>`, |
| | | if (opts.enableInHtmlEmbed) { |
| | | visit(tree, "html", (node: Html) => { |
| | | for (const [regex, replace] of replacements) { |
| | | if (typeof replace === "string") { |
| | | node.value = node.value.replace(regex, replace) |
| | | } else { |
| | | node.value = node.value.replaceAll(regex, (substring: string, ...args) => { |
| | | const replaceValue = replace(substring, ...args) |
| | | if (typeof replaceValue === "string") { |
| | | return replaceValue |
| | | } else if (Array.isArray(replaceValue)) { |
| | | return replaceValue.map(mdastToHtml).join("") |
| | | } else if (typeof replaceValue === "object" && replaceValue !== null) { |
| | | return mdastToHtml(replaceValue) |
| | | } else { |
| | | return substring |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | if (opts.comments) { |
| | | plugins.push(() => { |
| | | return (tree: Root, _file) => { |
| | | findAndReplace(tree, commentRegex, (_value: string, ..._capture: string[]) => { |
| | | return { |
| | | type: "text", |
| | | value: "", |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | mdastFindReplace(tree, replacements) |
| | | } |
| | | }) |
| | | |
| | | if (opts.callouts) { |
| | | plugins.push(() => { |
| | |
| | | <polyline points="6 9 12 15 18 9"></polyline> |
| | | </svg>` |
| | | |
| | | const titleHtml: HTML = { |
| | | const titleHtml: Html = { |
| | | type: "html", |
| | | value: `<div |
| | | class="callout-title" |
| | |
| | | }) |
| | | } |
| | | |
| | | if (opts.parseTags) { |
| | | plugins.push(() => { |
| | | return (tree: Root, file) => { |
| | | const base = pathToRoot(file.data.slug!) |
| | | findAndReplace(tree, tagRegex, (_value: string, tag: string) => { |
| | | // Check if the tag only includes numbers |
| | | if (/^\d+$/.test(tag)) { |
| | | return false |
| | | } |
| | | |
| | | tag = slugTag(tag) |
| | | if (file.data.frontmatter && !file.data.frontmatter.tags.includes(tag)) { |
| | | file.data.frontmatter.tags.push(tag) |
| | | } |
| | | |
| | | return { |
| | | type: "link", |
| | | url: base + `/tags/${tag}`, |
| | | data: { |
| | | hProperties: { |
| | | className: ["tag-link"], |
| | | }, |
| | | }, |
| | | children: [ |
| | | { |
| | | type: "text", |
| | | value: `#${tag}`, |
| | | }, |
| | | ], |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | return plugins |
| | | }, |
| | | htmlPlugins() { |
| | | const plugins = [rehypeRaw] |
| | | |
| | | const plugins: PluggableList = [rehypeRaw] |
| | | if (opts.parseBlockReferences) { |
| | | plugins.push(() => { |
| | | const inlineTagTypes = new Set(["p", "li"]) |
| | |
| | | [ |
| | | rehypePrettyCode, |
| | | { |
| | | theme: "css-variables", |
| | | keepBackground: false, |
| | | theme: { |
| | | dark: "github-dark", |
| | | light: "github-light", |
| | | }, |
| | | } satisfies Partial<CodeOptions>, |
| | | ], |
| | | ] |
| | |
| | | import { Data, VFile } from "vfile" |
| | | |
| | | export type QuartzPluginData = Data |
| | | export type ProcessedContent = [Node<QuartzPluginData>, VFile] |
| | | export type ProcessedContent = [Node, VFile] |
| | | |
| | | export function defaultProcessedContent(vfileData: Partial<QuartzPluginData>): ProcessedContent { |
| | | const root: Parent = { type: "root", children: [] } |
| | |
| | | import { trace } from "../util/trace" |
| | | import { BuildCtx } from "../util/ctx" |
| | | |
| | | export type QuartzProcessor = Processor<MDRoot, HTMLRoot, void> |
| | | export type QuartzProcessor = Processor<MDRoot, MDRoot, HTMLRoot> |
| | | export function createProcessor(ctx: BuildCtx): QuartzProcessor { |
| | | const transformers = ctx.cfg.plugins.transformers |
| | | |
| | | // base Markdown -> MD AST |
| | | let processor = unified().use(remarkParse) |
| | | |
| | | // MD AST -> MD AST transforms |
| | | for (const plugin of transformers.filter((p) => p.markdownPlugins)) { |
| | | processor = processor.use(plugin.markdownPlugins!(ctx)) |
| | | } |
| | | |
| | | // MD AST -> HTML AST |
| | | processor = processor.use(remarkRehype, { allowDangerousHtml: true }) |
| | | |
| | | // HTML AST -> HTML AST transforms |
| | | for (const plugin of transformers.filter((p) => p.htmlPlugins)) { |
| | | processor = processor.use(plugin.htmlPlugins!(ctx)) |
| | | } |
| | | |
| | | return processor |
| | | return ( |
| | | unified() |
| | | // base Markdown -> MD AST |
| | | .use(remarkParse) |
| | | // MD AST -> MD AST transforms |
| | | .use( |
| | | transformers |
| | | .filter((p) => p.markdownPlugins) |
| | | .flatMap((plugin) => plugin.markdownPlugins!(ctx)), |
| | | ) |
| | | // MD AST -> HTML AST |
| | | .use(remarkRehype, { allowDangerousHtml: true }) |
| | | // HTML AST -> HTML AST transforms |
| | | .use(transformers.filter((p) => p.htmlPlugins).flatMap((plugin) => plugin.htmlPlugins!(ctx))) |
| | | ) |
| | | } |
| | | |
| | | function* chunks<T>(arr: T[], n: number) { |
| | |
| | | |
| | | // Text -> Text transforms |
| | | for (const plugin of cfg.plugins.transformers.filter((p) => p.textTransform)) { |
| | | file.value = plugin.textTransform!(ctx, file.value) |
| | | file.value = plugin.textTransform!(ctx, file.value.toString()) |
| | | } |
| | | |
| | | // base data properties that plugins may use |
| | |
| | | margin-bottom: 1rem; |
| | | } |
| | | |
| | | div[data-rehype-pretty-code-fragment] { |
| | | figure[data-rehype-pretty-code-figure] { |
| | | margin: 0; |
| | | position: relative; |
| | | line-height: 1.6rem; |
| | | position: relative; |
| | | |
| | | & > div[data-rehype-pretty-code-title] { |
| | | & > [data-rehype-pretty-code-title] { |
| | | font-family: var(--codeFont); |
| | | font-size: 0.9rem; |
| | | padding: 0.1rem 0.5rem; |
| | |
| | | } |
| | | |
| | | & > pre { |
| | | padding: 0.5rem 0; |
| | | padding: 0; |
| | | } |
| | | } |
| | | |
| | |
| | | counter-reset: line; |
| | | counter-increment: line 0; |
| | | display: grid; |
| | | padding: 0.5rem 0; |
| | | |
| | | & [data-highlighted-chars] { |
| | | background-color: var(--highlight); |
| | |
| | | // npx convert-sh-theme https://raw.githubusercontent.com/shikijs/shiki/main/packages/shiki/themes/github-light.json |
| | | :root { |
| | | --shiki-color-text: #24292e; |
| | | --shiki-color-background: #f8f8f8; |
| | | --shiki-token-constant: #005cc5; |
| | | --shiki-token-string: #032f62; |
| | | --shiki-token-comment: #6a737d; |
| | | --shiki-token-keyword: #d73a49; |
| | | --shiki-token-parameter: #24292e; |
| | | --shiki-token-function: #24292e; |
| | | --shiki-token-string-expression: #22863a; |
| | | --shiki-token-punctuation: #24292e; |
| | | --shiki-token-link: #24292e; |
| | | code[data-theme*=" "] { |
| | | color: var(--shiki-light); |
| | | background-color: var(--shiki-light-bg); |
| | | } |
| | | |
| | | // npx convert-sh-theme https://raw.githubusercontent.com/shikijs/shiki/main/packages/shiki/themes/github-dark.json |
| | | [saved-theme="dark"] { |
| | | --shiki-color-text: #e1e4e8 !important; |
| | | --shiki-color-background: #24292e !important; |
| | | --shiki-token-constant: #79b8ff !important; |
| | | --shiki-token-string: #9ecbff !important; |
| | | --shiki-token-comment: #6a737d !important; |
| | | --shiki-token-keyword: #f97583 !important; |
| | | --shiki-token-parameter: #e1e4e8 !important; |
| | | --shiki-token-function: #e1e4e8 !important; |
| | | --shiki-token-string-expression: #85e89d !important; |
| | | --shiki-token-punctuation: #e1e4e8 !important; |
| | | --shiki-token-link: #e1e4e8 !important; |
| | | code[data-theme*=" "] span { |
| | | color: var(--shiki-light); |
| | | } |
| | | |
| | | [saved-theme="dark"] code[data-theme*=" "] { |
| | | color: var(--shiki-dark); |
| | | background-color: var(--shiki-dark-bg); |
| | | } |
| | | |
| | | [saved-theme="dark"] code[data-theme*=" "] span { |
| | | color: var(--shiki-dark); |
| | | } |
| | |
| | | import { Components, Jsx, toJsxRuntime } from "hast-util-to-jsx-runtime" |
| | | import { QuartzPluginData } from "../plugins/vfile" |
| | | import { Node, Root } from "hast" |
| | | import { Fragment, jsx, jsxs } from "preact/jsx-runtime" |
| | | import { trace } from "./trace" |
| | |
| | | ), |
| | | } |
| | | |
| | | export function htmlToJsx(fp: FilePath, tree: Node<QuartzPluginData>) { |
| | | export function htmlToJsx(fp: FilePath, tree: Node) { |
| | | try { |
| | | return toJsxRuntime(tree as Root, { |
| | | Fragment, |