From 56f74e2bc80523049681b00db85f61d0540f32c7 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 26 Oct 2021 19:11:18 -0700 Subject: [PATCH] [next] fix island hydration inside of `` (#1665) * fix: create rehype plugin to smooth over island hydration bugs * refactor: remove debug code * chore: explain need for `rehypeIslands` --- packages/markdown/remark/src/index.ts | 4 ++- .../markdown/remark/src/rehype-islands.ts | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 packages/markdown/remark/src/rehype-islands.ts diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index b5a01b7b3..a5fb403d4 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -4,6 +4,7 @@ import createCollectHeaders from './rehype-collect-headers.js'; import scopedStyles from './remark-scoped-styles.js'; import { remarkExpressions, loadRemarkExpressions } from './remark-expressions.js'; import rehypeExpressions from './rehype-expressions.js'; +import rehypeIslands from './rehype-islands.js'; import { remarkJsx, loadRemarkJsx } from './remark-jsx.js'; import rehypeJsx from './rehype-jsx.js'; import remarkPrism from './remark-prism.js'; @@ -75,12 +76,13 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp .use(isMDX ? [rehypeJsx] : []) .use(isMDX ? [rehypeExpressions] : []) .use(isMDX ? [] : [rehypeRaw]) + .use(rehypeIslands) let result: string; try { const vfile = await parser .use([rehypeCollectHeaders]) - .use(rehypeStringify, { allowParseErrors: true, allowDangerousHtml: true }) + .use(rehypeStringify, { allowDangerousHtml: true }) .process(content); result = vfile.toString(); } catch (err) { diff --git a/packages/markdown/remark/src/rehype-islands.ts b/packages/markdown/remark/src/rehype-islands.ts new file mode 100644 index 000000000..099dc4d75 --- /dev/null +++ b/packages/markdown/remark/src/rehype-islands.ts @@ -0,0 +1,31 @@ +import {SKIP, visit} from 'unist-util-visit'; + +// This fixes some confusing bugs coming from somewhere inside of our Markdown pipeline. +// `unist`/`remark`/`rehype` (not sure) often generate malformed HTML inside of +// For hydration to work properly, frameworks need the DOM to be the exact same on server/client. +// This reverts some "helpful corrections" that are applied to our perfectly valid HTML! +export default function rehypeIslands(): any { + return function (node: any): any { + return visit(node, 'element', (el) => { + // Bugs only happen inside of islands + if (el.tagName == 'astro-root') { + visit(el, 'text', (child, index, parent) => { + if (child.type === 'text') { + // Sometimes comments can be trapped as text, which causes them to be escaped + // This casts them back to real HTML comments + if (parent && child.value.indexOf('', '').trim()}); + return [SKIP, index] + } + // For some reason `rehype` likes to inject extra linebreaks, + // but React and Vue throw hydration errors when they see these! + // This removes any extra linebreaks, which is fine because + // framework compilers don't preserve them anyway + child.value = child.value.replace(/\n+/g, ''); + return child; + } + }) + } + }); + }; +}