From d7cfeec91f58b5d38d8ccd489d0fd93a29145967 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Fri, 22 Oct 2021 11:40:03 -0700 Subject: [PATCH 1/2] fix: cleanup issues with component --- examples/with-markdown/src/pages/index.astro | 2 + packages/astro/components/Markdown.astro | 30 +++++++-------- packages/astro/src/core/ssr/index.ts | 15 ++++++++ packages/markdown/remark/package.json | 4 +- packages/markdown/remark/src/index.ts | 16 ++++++-- packages/markdown/remark/src/remark-unwrap.ts | 38 +++++++++++++++++++ packages/markdown/remark/src/types.ts | 1 + 7 files changed, 84 insertions(+), 22 deletions(-) create mode 100644 packages/markdown/remark/src/remark-unwrap.ts diff --git a/examples/with-markdown/src/pages/index.astro b/examples/with-markdown/src/pages/index.astro index de6f323032d5..7249f07cd393 100644 --- a/examples/with-markdown/src/pages/index.astro +++ b/examples/with-markdown/src/pages/index.astro @@ -44,9 +44,11 @@ const items = ['A', 'B', 'C']; ## Oh yeah... + 🤯 It's also _recursive_! ### Markdown can be embedded in any child component + ## Code diff --git a/packages/astro/components/Markdown.astro b/packages/astro/components/Markdown.astro index dbdb4e844c25..78018c469e18 100644 --- a/packages/astro/components/Markdown.astro +++ b/packages/astro/components/Markdown.astro @@ -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; @@ -15,18 +14,17 @@ let { content, class: className } = Astro.props as InternalProps; let html = null; // 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, privateRenderMarkdownDoNotUse: renderMarkdown } = (Astro as any); + content = dedent(await renderSlot('default')); + const htmlContent = await renderMarkdown(content, { + mode: 'md', + $: { + scopedClassName: className + } + }); -const { code: htmlContent } = await renderMarkdown(content, { - mode: 'md', - $: { - scopedClassName: className - } -}); - -html = htmlContent; + html = htmlContent; +} --- -{html ? html : } \ No newline at end of file +{html ? html : } diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts index c6049e50d050..e9d5dbd7e5bb 100644 --- a/packages/astro/src/core/ssr/index.ts +++ b/packages/astro/src/core/ssr/index.ts @@ -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 but shouldn't be used publicly privateRenderSlotDoNotUse(slotName: string) { return renderSlot(result, slots ? slots[slotName] : null); }, + // 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 }, diff --git a/packages/markdown/remark/package.json b/packages/markdown/remark/package.json index 32f12450bd61..fad9ef317791 100644 --- a/packages/markdown/remark/package.json +++ b/packages/markdown/remark/package.json @@ -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", diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index 0c385be21c49..23dfee2e20a6 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -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) { diff --git a/packages/markdown/remark/src/remark-unwrap.ts b/packages/markdown/remark/src/remark-unwrap.ts new file mode 100644 index 000000000000..e43a57a0ce7b --- /dev/null +++ b/packages/markdown/remark/src/remark-unwrap.ts @@ -0,0 +1,38 @@ +import {visit, SKIP} from 'unist-util-visit' + +// Remove the wrapping paragraph for 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(' -1 && !insideAstroRoot) { + insideAstroRoot = true; + } + if (node.value.indexOf(' -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) + } +} diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index fb5cdf4ac52a..201e50931579 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -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[]; } From 8fc08b69e518d212fca92a1abccadb40918879bf Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Fri, 22 Oct 2021 11:54:29 -0700 Subject: [PATCH 2/2] fix: fix `content` usage with Markdown --- packages/astro/components/Markdown.astro | 21 ++++++++++++--------- yarn.lock | 15 +++++++++++---- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/packages/astro/components/Markdown.astro b/packages/astro/components/Markdown.astro index 78018c469e18..85bc66d5e559 100644 --- a/packages/astro/components/Markdown.astro +++ b/packages/astro/components/Markdown.astro @@ -13,18 +13,21 @@ 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 { privateRenderSlotDoNotUse: renderSlot, privateRenderMarkdownDoNotUse: renderMarkdown } = (Astro as any); + const { privateRenderSlotDoNotUse: renderSlot } = (Astro as any); content = dedent(await renderSlot('default')); - const htmlContent = await renderMarkdown(content, { - mode: 'md', - $: { - scopedClassName: className - } - }); - - html = htmlContent; } + +const htmlContent = await renderMarkdown(content, { + mode: 'md', + $: { + scopedClassName: className + } +}); + +html = htmlContent; --- {html ? html : } diff --git a/yarn.lock b/yarn.lock index 98fb91f513d2..593a587823f0 100644 --- a/yarn.lock +++ b/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"