From 60b3bc34cb07b5bec87cbd667ea9f804ff14cf3c Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sat, 21 Oct 2023 21:05:46 -0700 Subject: [PATCH] fix: catch html to jsx errors (closes #547) --- docs/advanced/making plugins.md | 2 +- quartz/components/TagList.tsx | 5 ++++- quartz/components/pages/Content.tsx | 8 +++----- quartz/components/pages/FolderContent.tsx | 6 ++---- quartz/components/pages/TagContent.tsx | 6 ++---- quartz/util/jsx.ts | 15 +++++++++++++++ quartz/util/trace.ts | 8 ++------ 7 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 quartz/util/jsx.ts diff --git a/docs/advanced/making plugins.md b/docs/advanced/making plugins.md index 1f1616f42..d0934ad81 100644 --- a/docs/advanced/making plugins.md +++ b/docs/advanced/making plugins.md @@ -247,7 +247,7 @@ If you are creating an emitter plugin that needs to render components, there are - Your component should use `getQuartzComponents` to declare a list of `QuartzComponents` that it uses to construct the page. See the page on [[creating components]] for more information. - You can use the `renderPage` function defined in `quartz/components/renderPage.tsx` to render Quartz components into HTML. -- If you need to render an HTML AST to JSX, you can use the `toJsxRuntime` function from `hast-util-to-jsx-runtime` library. An example of this can be found in `quartz/components/pages/Content.tsx`. +- If you need to render an HTML AST to JSX, you can use the `htmlToJsx` function from `quartz/util/jsx.ts`. An example of this can be found in `quartz/components/pages/Content.tsx`. For example, the following is a simplified version of the content page plugin that renders every single page. diff --git a/quartz/components/TagList.tsx b/quartz/components/TagList.tsx index e39186e5f..cb1c121cf 100644 --- a/quartz/components/TagList.tsx +++ b/quartz/components/TagList.tsx @@ -33,9 +33,12 @@ TagList.css = ` gap: 0.4rem; margin: 1rem 0; flex-wrap: wrap; - justify-content: flex-end; justify-self: end; } + +.section-ul .tags { + justify-content: flex-end; +} .tags > li { display: inline-block; diff --git a/quartz/components/pages/Content.tsx b/quartz/components/pages/Content.tsx index 7490a7ea7..76cecc38c 100644 --- a/quartz/components/pages/Content.tsx +++ b/quartz/components/pages/Content.tsx @@ -1,10 +1,8 @@ +import { htmlToJsx } from "../../util/jsx" import { QuartzComponentConstructor, QuartzComponentProps } from "../types" -import { Fragment, jsx, jsxs } from "preact/jsx-runtime" -import { toJsxRuntime } from "hast-util-to-jsx-runtime" -function Content({ tree }: QuartzComponentProps) { - // @ts-ignore (preact makes it angry) - const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" }) +function Content({ fileData, tree }: QuartzComponentProps) { + const content = htmlToJsx(fileData.filePath!, tree) return
{content}
} diff --git a/quartz/components/pages/FolderContent.tsx b/quartz/components/pages/FolderContent.tsx index a766d4b0b..765f84657 100644 --- a/quartz/components/pages/FolderContent.tsx +++ b/quartz/components/pages/FolderContent.tsx @@ -1,6 +1,4 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "../types" -import { Fragment, jsx, jsxs } from "preact/jsx-runtime" -import { toJsxRuntime } from "hast-util-to-jsx-runtime" import path from "path" import style from "../styles/listPage.scss" @@ -8,6 +6,7 @@ import { PageList } from "../PageList" import { _stripSlashes, simplifySlug } from "../../util/path" import { Root } from "hast" import { pluralize } from "../../util/lang" +import { htmlToJsx } from "../../util/jsx" function FolderContent(props: QuartzComponentProps) { const { tree, fileData, allFiles } = props @@ -29,8 +28,7 @@ function FolderContent(props: QuartzComponentProps) { const content = (tree as Root).children.length === 0 ? fileData.description - : // @ts-ignore - toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" }) + : htmlToJsx(fileData.filePath!, tree) return (
diff --git a/quartz/components/pages/TagContent.tsx b/quartz/components/pages/TagContent.tsx index 9907e3fc3..205ba8958 100644 --- a/quartz/components/pages/TagContent.tsx +++ b/quartz/components/pages/TagContent.tsx @@ -1,12 +1,11 @@ import { QuartzComponentConstructor, QuartzComponentProps } from "../types" -import { Fragment, jsx, jsxs } from "preact/jsx-runtime" -import { toJsxRuntime } from "hast-util-to-jsx-runtime" import style from "../styles/listPage.scss" import { PageList } from "../PageList" import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path" import { QuartzPluginData } from "../../plugins/vfile" import { Root } from "hast" import { pluralize } from "../../util/lang" +import { htmlToJsx } from "../../util/jsx" const numPages = 10 function TagContent(props: QuartzComponentProps) { @@ -26,8 +25,7 @@ function TagContent(props: QuartzComponentProps) { const content = (tree as Root).children.length === 0 ? fileData.description - : // @ts-ignore - toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" }) + : htmlToJsx(fileData.filePath!, tree) if (tag === "") { const tags = [...new Set(allFiles.flatMap((data) => data.frontmatter?.tags ?? []))] diff --git a/quartz/util/jsx.ts b/quartz/util/jsx.ts new file mode 100644 index 000000000..8cba485ab --- /dev/null +++ b/quartz/util/jsx.ts @@ -0,0 +1,15 @@ +import { 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" +import { type FilePath } from "./path" + +export function htmlToJsx(fp: FilePath, tree: Node) { + try { + // @ts-ignore (preact makes it angry) + return toJsxRuntime(tree as Root, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" }) + } catch (e) { + trace(`Failed to parse Markdown in \`${fp}\` into JSX`, e as Error) + } +} diff --git a/quartz/util/trace.ts b/quartz/util/trace.ts index c7f3cc339..a33135d64 100644 --- a/quartz/util/trace.ts +++ b/quartz/util/trace.ts @@ -4,7 +4,7 @@ import { isMainThread } from "workerpool" const rootFile = /.*at file:/ export function trace(msg: string, err: Error) { - const stack = err.stack + let stack = err.stack ?? "" const lines: string[] = [] @@ -12,15 +12,11 @@ export function trace(msg: string, err: Error) { lines.push( "\n" + chalk.bgRed.black.bold(" ERROR ") + - "\n" + + "\n\n" + chalk.red(` ${msg}`) + (err.message.length > 0 ? `: ${err.message}` : ""), ) - if (!stack) { - return - } - let reachedEndOfLegibleTrace = false for (const line of stack.split("\n").slice(1)) { if (reachedEndOfLegibleTrace) {