fix: catch html to jsx errors (closes #547)

This commit is contained in:
Jacky Zhao 2023-10-21 21:05:46 -07:00
parent dc834015d0
commit 60b3bc34cb
7 changed files with 29 additions and 21 deletions

View file

@ -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. - 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. - 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. For example, the following is a simplified version of the content page plugin that renders every single page.

View file

@ -33,10 +33,13 @@ TagList.css = `
gap: 0.4rem; gap: 0.4rem;
margin: 1rem 0; margin: 1rem 0;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-end;
justify-self: end; justify-self: end;
} }
.section-ul .tags {
justify-content: flex-end;
}
.tags > li { .tags > li {
display: inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;

View file

@ -1,10 +1,8 @@
import { htmlToJsx } from "../../util/jsx"
import { QuartzComponentConstructor, QuartzComponentProps } from "../types" 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) { function Content({ fileData, tree }: QuartzComponentProps) {
// @ts-ignore (preact makes it angry) const content = htmlToJsx(fileData.filePath!, tree)
const content = toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
return <article class="popover-hint">{content}</article> return <article class="popover-hint">{content}</article>
} }

View file

@ -1,6 +1,4 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types" 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 path from "path"
import style from "../styles/listPage.scss" import style from "../styles/listPage.scss"
@ -8,6 +6,7 @@ import { PageList } from "../PageList"
import { _stripSlashes, simplifySlug } from "../../util/path" import { _stripSlashes, simplifySlug } from "../../util/path"
import { Root } from "hast" import { Root } from "hast"
import { pluralize } from "../../util/lang" import { pluralize } from "../../util/lang"
import { htmlToJsx } from "../../util/jsx"
function FolderContent(props: QuartzComponentProps) { function FolderContent(props: QuartzComponentProps) {
const { tree, fileData, allFiles } = props const { tree, fileData, allFiles } = props
@ -29,8 +28,7 @@ function FolderContent(props: QuartzComponentProps) {
const content = const content =
(tree as Root).children.length === 0 (tree as Root).children.length === 0
? fileData.description ? fileData.description
: // @ts-ignore : htmlToJsx(fileData.filePath!, tree)
toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
return ( return (
<div class="popover-hint"> <div class="popover-hint">

View file

@ -1,12 +1,11 @@
import { QuartzComponentConstructor, QuartzComponentProps } from "../types" 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 style from "../styles/listPage.scss"
import { PageList } from "../PageList" import { PageList } from "../PageList"
import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path" import { FullSlug, getAllSegmentPrefixes, simplifySlug } from "../../util/path"
import { QuartzPluginData } from "../../plugins/vfile" import { QuartzPluginData } from "../../plugins/vfile"
import { Root } from "hast" import { Root } from "hast"
import { pluralize } from "../../util/lang" import { pluralize } from "../../util/lang"
import { htmlToJsx } from "../../util/jsx"
const numPages = 10 const numPages = 10
function TagContent(props: QuartzComponentProps) { function TagContent(props: QuartzComponentProps) {
@ -26,8 +25,7 @@ function TagContent(props: QuartzComponentProps) {
const content = const content =
(tree as Root).children.length === 0 (tree as Root).children.length === 0
? fileData.description ? fileData.description
: // @ts-ignore : htmlToJsx(fileData.filePath!, tree)
toJsxRuntime(tree, { Fragment, jsx, jsxs, elementAttributeNameCase: "html" })
if (tag === "") { if (tag === "") {
const tags = [...new Set(allFiles.flatMap((data) => data.frontmatter?.tags ?? []))] const tags = [...new Set(allFiles.flatMap((data) => data.frontmatter?.tags ?? []))]

15
quartz/util/jsx.ts Normal file
View file

@ -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<QuartzPluginData>) {
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)
}
}

View file

@ -4,7 +4,7 @@ import { isMainThread } from "workerpool"
const rootFile = /.*at file:/ const rootFile = /.*at file:/
export function trace(msg: string, err: Error) { export function trace(msg: string, err: Error) {
const stack = err.stack let stack = err.stack ?? ""
const lines: string[] = [] const lines: string[] = []
@ -12,15 +12,11 @@ export function trace(msg: string, err: Error) {
lines.push( lines.push(
"\n" + "\n" +
chalk.bgRed.black.bold(" ERROR ") + chalk.bgRed.black.bold(" ERROR ") +
"\n" + "\n\n" +
chalk.red(` ${msg}`) + chalk.red(` ${msg}`) +
(err.message.length > 0 ? `: ${err.message}` : ""), (err.message.length > 0 ? `: ${err.message}` : ""),
) )
if (!stack) {
return
}
let reachedEndOfLegibleTrace = false let reachedEndOfLegibleTrace = false
for (const line of stack.split("\n").slice(1)) { for (const line of stack.split("\n").slice(1)) {
if (reachedEndOfLegibleTrace) { if (reachedEndOfLegibleTrace) {