fix: catch html to jsx errors (closes #547)
This commit is contained in:
		@@ -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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,9 +33,12 @@ 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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
									
								
							
							
						
						
									
										15
									
								
								quartz/util/jsx.ts
									
									
									
									
									
										Normal 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)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user