[next] Fix <Markdown>
component (#1631)
* fix: cleanup issues with <Markdown> component * fix: fix `content` usage with Markdown
This commit is contained in:
parent
4647c998ea
commit
09bc35e803
8 changed files with 90 additions and 18 deletions
|
@ -44,9 +44,11 @@ const items = ['A', 'B', 'C'];
|
|||
## Oh yeah...
|
||||
|
||||
<ReactCounter client:visible>
|
||||
|
||||
🤯 It's also _recursive_!
|
||||
|
||||
### Markdown can be embedded in any child component
|
||||
|
||||
</ReactCounter>
|
||||
|
||||
## Code
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
---
|
||||
import { renderMarkdown } from '@astrojs/markdown-remark';
|
||||
import stripIndent from 'strip-indent';
|
||||
|
||||
export interface Props {
|
||||
content?: string;
|
||||
}
|
||||
|
||||
const dedent = (str: string) => str.split('\n').map(ln => ln.trimStart()).join('\n');
|
||||
|
||||
// Internal props that should not be part of the external interface.
|
||||
interface InternalProps extends Props {
|
||||
$scope: string;
|
||||
|
@ -14,13 +13,15 @@ interface InternalProps extends Props {
|
|||
let { content, class: className } = Astro.props as InternalProps;
|
||||
let html = null;
|
||||
|
||||
const { privateRenderMarkdownDoNotUse: renderMarkdown } = (Astro as any);
|
||||
|
||||
// If no content prop provided, use the slot.
|
||||
if(!content) {
|
||||
const renderSlot = (Astro as any).privateRenderSlotDoNotUse;
|
||||
content = stripIndent(await renderSlot('default'));
|
||||
if (!content) {
|
||||
const { privateRenderSlotDoNotUse: renderSlot } = (Astro as any);
|
||||
content = dedent(await renderSlot('default'));
|
||||
}
|
||||
|
||||
const { code: htmlContent } = await renderMarkdown(content, {
|
||||
const htmlContent = await renderMarkdown(content, {
|
||||
mode: 'md',
|
||||
$: {
|
||||
scopedClassName: className
|
||||
|
@ -29,4 +30,4 @@ const { code: htmlContent } = await renderMarkdown(content, {
|
|||
|
||||
html = htmlContent;
|
||||
---
|
||||
{html ? html : <slot />}
|
||||
{html ? html : <slot />}
|
||||
|
|
|
@ -132,9 +132,24 @@ export async function ssr({ astroConfig, filePath, logging, mode, origin, pathna
|
|||
url,
|
||||
},
|
||||
slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
|
||||
// This is used for <Markdown> but shouldn't be used publicly
|
||||
privateRenderSlotDoNotUse(slotName: string) {
|
||||
return renderSlot(result, slots ? slots[slotName] : null);
|
||||
},
|
||||
// <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
|
||||
async privateRenderMarkdownDoNotUse(content: string, opts: any) {
|
||||
let render = astroConfig.markdownOptions.render;
|
||||
let renderOpts = {};
|
||||
if (Array.isArray(render)) {
|
||||
renderOpts = render[1];
|
||||
render = render[0];
|
||||
}
|
||||
if (typeof render === 'string') {
|
||||
({ default: render } = await import(render));
|
||||
}
|
||||
const { code } = await render(content, { ...renderOpts, ...(opts ?? {}) });
|
||||
return code
|
||||
}
|
||||
} as unknown as AstroGlobal;
|
||||
},
|
||||
_metadata: { renderers },
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
"@silvenon/remark-smartypants": "^1.0.0",
|
||||
"assert": "^2.0.0",
|
||||
"github-slugger": "^1.3.0",
|
||||
"mdast-util-mdx-expression": "^1.1.0",
|
||||
"mdast-util-mdx-jsx": "^1.1.0",
|
||||
"mdast-util-mdx-expression": "^1.1.1",
|
||||
"mdast-util-mdx-jsx": "^1.1.1",
|
||||
"micromark-extension-mdx-expression": "^1.0.0",
|
||||
"micromark-extension-mdx-jsx": "^1.0.0",
|
||||
"prismjs": "^1.25.0",
|
||||
|
|
|
@ -8,12 +8,14 @@ import { remarkJsx, loadRemarkJsx } from './remark-jsx.js';
|
|||
import rehypeJsx from './rehype-jsx.js';
|
||||
//import { remarkCodeBlock } from './codeblock.js';
|
||||
import remarkPrism from './remark-prism.js';
|
||||
import remarkUnwrap from './remark-unwrap.js';
|
||||
import { loadPlugins } from './load-plugins.js';
|
||||
|
||||
import { unified } from 'unified';
|
||||
import markdown from 'remark-parse';
|
||||
import markdownToHtml from 'remark-rehype';
|
||||
import rehypeStringify from 'rehype-stringify';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import matter from 'gray-matter';
|
||||
|
||||
export { AstroMarkdownOptions, MarkdownRenderingOptions };
|
||||
|
@ -40,14 +42,17 @@ export const DEFAULT_REHYPE_PLUGINS = [
|
|||
export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) {
|
||||
const { remarkPlugins = DEFAULT_REMARK_PLUGINS, rehypePlugins = DEFAULT_REHYPE_PLUGINS } = opts ?? {};
|
||||
const scopedClassName = opts?.$?.scopedClassName;
|
||||
const mode = opts?.mode ?? "mdx";
|
||||
const isMDX = mode === 'mdx';
|
||||
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
||||
|
||||
await Promise.all([loadRemarkExpressions(), loadRemarkJsx()]); // Vite bug: dynamically import() these because of CJS interop (this will cache)
|
||||
|
||||
let parser = unified()
|
||||
.use(markdown)
|
||||
.use([remarkJsx])
|
||||
.use([remarkExpressions])
|
||||
.use(isMDX ? [remarkJsx] : [])
|
||||
.use(isMDX ? [remarkExpressions] : [])
|
||||
.use([remarkUnwrap])
|
||||
.use([remarkPrism(scopedClassName)])
|
||||
|
||||
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
|
||||
|
@ -68,13 +73,16 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp
|
|||
parser.use([[plugin, opts]]);
|
||||
});
|
||||
|
||||
parser.use([rehypeJsx]).use(rehypeExpressions)
|
||||
parser
|
||||
.use(isMDX ? [rehypeJsx] : [])
|
||||
.use(isMDX ? [rehypeExpressions] : [])
|
||||
.use(isMDX ? [] : [rehypeRaw])
|
||||
|
||||
let result: string;
|
||||
try {
|
||||
const vfile = await parser
|
||||
.use([rehypeCollectHeaders])
|
||||
.use(rehypeStringify, { allowParseErrors: true, preferUnquoted: false, allowDangerousHtml: true })
|
||||
.use(rehypeStringify, { allowParseErrors: true, allowDangerousHtml: true })
|
||||
.process(content);
|
||||
result = vfile.toString();
|
||||
} catch (err) {
|
||||
|
|
38
packages/markdown/remark/src/remark-unwrap.ts
Normal file
38
packages/markdown/remark/src/remark-unwrap.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import {visit, SKIP} from 'unist-util-visit'
|
||||
|
||||
// Remove the wrapping paragraph for <astro-root> islands
|
||||
export default function remarkUnwrap() {
|
||||
const astroRootNodes = new Set();
|
||||
let insideAstroRoot = false;
|
||||
|
||||
return (tree: any) => {
|
||||
// reset state
|
||||
insideAstroRoot = false;
|
||||
astroRootNodes.clear();
|
||||
|
||||
visit(tree, 'html', (node) => {
|
||||
if (node.value.indexOf('<astro-root') > -1 && !insideAstroRoot) {
|
||||
insideAstroRoot = true;
|
||||
}
|
||||
if (node.value.indexOf('</astro-root') > -1 && insideAstroRoot) {
|
||||
insideAstroRoot = false;
|
||||
}
|
||||
astroRootNodes.add(node);
|
||||
})
|
||||
|
||||
visit(tree, 'paragraph', (node, index, parent) => {
|
||||
if (
|
||||
parent &&
|
||||
typeof index === 'number' &&
|
||||
containsAstroRootNode(node)
|
||||
) {
|
||||
parent.children.splice(index, 1, ...node.children)
|
||||
return [SKIP, index]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function containsAstroRootNode(node: any) {
|
||||
return node.children.map((child: any) => astroRootNodes.has(child)).reduce((all: boolean, v: boolean) => all ? all : v, false)
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ export type UnifiedPluginImport = Promise<{ default: unified.Plugin }>;
|
|||
export type Plugin = string | [string, any] | UnifiedPluginImport | [UnifiedPluginImport, any];
|
||||
|
||||
export interface AstroMarkdownOptions {
|
||||
mode?: 'md'|'mdx';
|
||||
remarkPlugins?: Plugin[];
|
||||
rehypePlugins?: Plugin[];
|
||||
}
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -6927,10 +6927,17 @@ mdast-util-mdx-expression@^1.1.0:
|
|||
"@types/estree-jsx" "^0.0.1"
|
||||
strip-indent "^4.0.0"
|
||||
|
||||
mdast-util-mdx-jsx@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-1.1.0.tgz#e44bf620c3d218b0d9d836b30927098f1c080d72"
|
||||
integrity sha512-iPeyjvvPU7biH1bw/1VM9PLSM9ZXy7409RRB4GKMWl2CASjwz27i6DIHAyjAsYdwdtPXXvX/TJ6AsfWP9M5DSA==
|
||||
mdast-util-mdx-expression@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.1.1.tgz#657522e78b84f5c85cd2395776aba8dcfb7bbb0f"
|
||||
integrity sha512-RDLRkBFmBKCJl6/fQdxxKL2BqNtoPFoNBmQAlj5ZNKOijIWRKjdhPkeufsUOaexLj+78mhJc+L7d1MYka8/LdQ==
|
||||
dependencies:
|
||||
"@types/estree-jsx" "^0.0.1"
|
||||
|
||||
mdast-util-mdx-jsx@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-1.1.1.tgz#f2ee5e3b74282a8fecf3a6608bb19ed656751a34"
|
||||
integrity sha512-C4W4hXmagipaeMwi5O8y+lVWE4qP2MDxfKlIh0lZN6MZWSPpQTK5RPwKBH4DdYHorgjbV2rKk84rNWlRtvoZCg==
|
||||
dependencies:
|
||||
"@types/estree-jsx" "^0.0.1"
|
||||
"@types/mdast" "^3.0.0"
|
||||
|
|
Loading…
Reference in a new issue