mirror of
https://github.com/withastro/astro.git
synced 2025-03-10 23:01:26 -05:00
[next] fix island hydration inside of <Markdown>
(#1665)
* fix: create rehype plugin to smooth over island hydration bugs * refactor: remove debug code * chore: explain need for `rehypeIslands`
This commit is contained in:
parent
b04d478b52
commit
56f74e2bc8
2 changed files with 34 additions and 1 deletions
|
@ -4,6 +4,7 @@ import createCollectHeaders from './rehype-collect-headers.js';
|
||||||
import scopedStyles from './remark-scoped-styles.js';
|
import scopedStyles from './remark-scoped-styles.js';
|
||||||
import { remarkExpressions, loadRemarkExpressions } from './remark-expressions.js';
|
import { remarkExpressions, loadRemarkExpressions } from './remark-expressions.js';
|
||||||
import rehypeExpressions from './rehype-expressions.js';
|
import rehypeExpressions from './rehype-expressions.js';
|
||||||
|
import rehypeIslands from './rehype-islands.js';
|
||||||
import { remarkJsx, loadRemarkJsx } from './remark-jsx.js';
|
import { remarkJsx, loadRemarkJsx } from './remark-jsx.js';
|
||||||
import rehypeJsx from './rehype-jsx.js';
|
import rehypeJsx from './rehype-jsx.js';
|
||||||
import remarkPrism from './remark-prism.js';
|
import remarkPrism from './remark-prism.js';
|
||||||
|
@ -75,12 +76,13 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
|
||||||
.use(isMDX ? [rehypeJsx] : [])
|
.use(isMDX ? [rehypeJsx] : [])
|
||||||
.use(isMDX ? [rehypeExpressions] : [])
|
.use(isMDX ? [rehypeExpressions] : [])
|
||||||
.use(isMDX ? [] : [rehypeRaw])
|
.use(isMDX ? [] : [rehypeRaw])
|
||||||
|
.use(rehypeIslands)
|
||||||
|
|
||||||
let result: string;
|
let result: string;
|
||||||
try {
|
try {
|
||||||
const vfile = await parser
|
const vfile = await parser
|
||||||
.use([rehypeCollectHeaders])
|
.use([rehypeCollectHeaders])
|
||||||
.use(rehypeStringify, { allowParseErrors: true, allowDangerousHtml: true })
|
.use(rehypeStringify, { allowDangerousHtml: true })
|
||||||
.process(content);
|
.process(content);
|
||||||
result = vfile.toString();
|
result = vfile.toString();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
31
packages/markdown/remark/src/rehype-islands.ts
Normal file
31
packages/markdown/remark/src/rehype-islands.ts
Normal file
|
@ -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 <astro-root>
|
||||||
|
// 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 <astro-root> 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('<!--') > -1 && index != null) {
|
||||||
|
parent.children.splice(index, 1, { ...child, type: 'comment', value: child.value.replace('<!--', '').replace('-->', '').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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue