diff --git a/.changeset/clever-cheetahs-jump.md b/.changeset/clever-cheetahs-jump.md new file mode 100644 index 000000000..282307801 --- /dev/null +++ b/.changeset/clever-cheetahs-jump.md @@ -0,0 +1,25 @@ +--- +'astro': minor +'@astrojs/markdown-support': minor +--- + +Add support for [`remark`](https://github.com/remarkjs/remark#readme) and [`rehype`](https://github.com/rehypejs/rehype#readme) plugins for both `.md` pages and `.astro` pages using the [``](/docs/guides/markdown-content.md) component. + +For example, the `astro.config.mjs` could be updated to include the following. [Read the Markdown documentation](/docs/guides/markdown-content.md) for more information. + +> **Note** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants). You must explicitly add these plugins to your `astro.config.mjs` file, if desired. + +```js +export default { + markdownOptions: { + remarkPlugins: [ + 'remark-slug', + ['remark-autolink-headings', { behavior: 'prepend'}], + ], + rehypePlugins: [ + 'rehype-slug', + ['rehype-autolink-headings', { behavior: 'prepend'}], + ] + }, +} +``` diff --git a/docs/guides/markdown-content.md b/docs/guides/markdown-content.md index b00312034..00dbd7f87 100644 --- a/docs/guides/markdown-content.md +++ b/docs/guides/markdown-content.md @@ -4,12 +4,51 @@ title: Markdown Content --- Astro comes with out-of-the-box Markdown support powered by the expansive [remark](https://remark.js.org/) ecosystem. +## Remark and Rehype Plugins -### Remark Plugins +In addition to [custom components inside the `` component](#markdown-component), Astro comes with [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants), and syntax highlighting via [Prism](https://prismjs.com/) pre-enabled. -**This is the first draft of Markdown support!** While we plan to support user-provided `remark` plugins soon, our hope is that you won't need `remark` plugins at all! +Also, Astro supports third-party plugins for Markdown. You can provide your plugins in `astro.config.mjs`. -In addition to [custom components inside the `` component](https://github.com/snowpackjs/astro/blob/main/docs/markdown.md#markdown-component), Astro comes with [Github-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants), and syntax highlighting via [Prism](https://prismjs.com/) pre-enabled. These features are likely to be configurable in the future. +> **Note** Enabling custom `remarkPlugins` or `rehypePlugins` removes Astro's built-in support for [GitHub-flavored Markdown](https://github.github.com/gfm/) support, [Footnotes](https://github.com/remarkjs/remark-footnotes) syntax, [Smartypants](https://github.com/silvenon/remark-smartypants). You must explicitly add these plugins to your `astro.config.mjs` file, if desired. + +## Add a markdown plugin in Astro + +If you want to add a plugin, you need to install the npm package dependency in your project and then update the `markdownOptions.remarkPlugins` or `markdownOptions.rehypePlugins` depends on what plugin you want to have: + +```js +// astro.config.js +export default { + markdownOptions: { + remarkPlugins: [ + // Add a Remark plugin that you want to enable for your project. + // If you need to provide options for the plugin, you can use an array and put the options as the second item. + // 'remark-slug', + // ['remark-autolink-headings', { behavior: 'prepend'}], + ] + rehypePlugins: [ + // Add a Rehype plugin that you want to enable for your project. + // If you need to provide options for the plugin, you can use an array and put the options as the second item. + // 'rehype-slug', + // ['rehype-autolink-headings', { behavior: 'prepend'}], + ] + }, +}; +``` + +You can provide names of the plugins as well as import them: + +```js +// astro.config.js +export default { + markdownOptions: { + remarkPlugins: [ + import('remark-slug'), + [import('remark-autolink-headings'), { behavior: 'prepend' }], + ], + }, +}; +``` ### Markdown Pages diff --git a/examples/with-markdown-plugins/.gitignore b/examples/with-markdown-plugins/.gitignore new file mode 100644 index 000000000..d436c6dad --- /dev/null +++ b/examples/with-markdown-plugins/.gitignore @@ -0,0 +1,18 @@ +# build output +dist + +# dependencies +node_modules/ +.snowpack/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/examples/with-markdown-plugins/.npmrc b/examples/with-markdown-plugins/.npmrc new file mode 100644 index 000000000..0cc653b2c --- /dev/null +++ b/examples/with-markdown-plugins/.npmrc @@ -0,0 +1,2 @@ +## force pnpm to hoist +shamefully-hoist = true \ No newline at end of file diff --git a/examples/with-markdown-plugins/astro.config.mjs b/examples/with-markdown-plugins/astro.config.mjs new file mode 100644 index 000000000..9057b5c2f --- /dev/null +++ b/examples/with-markdown-plugins/astro.config.mjs @@ -0,0 +1,25 @@ +export default { + // projectRoot: '.', // Where to resolve all URLs relative to. Useful if you have a monorepo project. + // pages: './src/pages', // Path to Astro components, pages, and data + // dist: './dist', // When running `astro build`, path to final static output + // public: './public', // A folder of static files Astro will copy to the root. Useful for favicons, images, and other files that don’t need processing. + buildOptions: { + site: 'http://example.com', // Your public domain, e.g.: https://my-site.dev/. Used to generate sitemaps and canonical URLs. + // sitemap: true, // Generate sitemap (set to "false" to disable) + }, + markdownOptions: { + remarkPlugins: [ + 'remark-code-titles', + 'remark-slug', + ['remark-autolink-headings', { behavior: 'prepend' }], + ], + rehypePlugins: [ + ['rehype-toc', { headings: ["h2", "h3"] }], + ['rehype-add-classes', { 'h1,h2,h3': 'title', }], + ] + }, + devOptions: { + // port: 3000, // The port to run the dev server on. + // tailwindConfig: '', // Path to tailwind.config.js if used, e.g. './tailwind.config.js' + }, +}; diff --git a/examples/with-markdown-plugins/package.json b/examples/with-markdown-plugins/package.json new file mode 100644 index 000000000..f37b72225 --- /dev/null +++ b/examples/with-markdown-plugins/package.json @@ -0,0 +1,20 @@ +{ + "name": "@example/with-markdown-plugins", + "version": "0.0.2", + "private": true, + "scripts": { + "start": "astro dev", + "build": "astro build" + }, + "devDependencies": { + "astro": "^0.15.0", + "rehype-add-classes": "^1.0.0", + "rehype-toc": "^3.0.2", + "remark-autolink-headings": "^6.0.1", + "remark-code-titles": "^0.1.2", + "remark-slug": "^6.0.0" + }, + "snowpack": { + "workspaceRoot": "../.." + } +} diff --git a/examples/with-markdown-plugins/public/favicon.svg b/examples/with-markdown-plugins/public/favicon.svg new file mode 100644 index 000000000..542f90aec --- /dev/null +++ b/examples/with-markdown-plugins/public/favicon.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/examples/with-markdown-plugins/public/global.css b/examples/with-markdown-plugins/public/global.css new file mode 100644 index 000000000..f1e536546 --- /dev/null +++ b/examples/with-markdown-plugins/public/global.css @@ -0,0 +1,53 @@ +body { + font-family: system-ui; +} + +.content { + max-width: 640px; + margin: 40px auto; + padding: 0 20px; +} + +.title { + position: relative; +} +.title a { + position: absolute; + display: block; + height: 100%; + width: 100%; + color: inherit; +} + +.title a:before { + position: absolute; + right: 100%; + display: block; + content: '#'; + margin-right: 0.2em; + visibility: hidden; + opacity: 0.5; +} + +.title:hover a:before { + visibility: visible; +} + +.remark-code-title, +pre[class^="language-"] { + padding: 10px; + margin: 0; +} + +.remark-code-title { + border-bottom: 1px solid rgba(0,0,0,.05); + border-radius: 4px 4px 0 0; + background: rgba(0,0,0,.08); + font-family: monospace; + font-weight: bold; +} + +pre[class^="language-"] { + background: rgba(0,0,0,.05); + border-radius: 0 0 4px 4px; +} diff --git a/examples/with-markdown-plugins/src/layouts/main.astro b/examples/with-markdown-plugins/src/layouts/main.astro new file mode 100644 index 000000000..d324d2017 --- /dev/null +++ b/examples/with-markdown-plugins/src/layouts/main.astro @@ -0,0 +1,32 @@ +--- +const { content } = Astro.props; +--- + + + + + {content.title} + + + + +
+
+ +
+ +
+ + diff --git a/examples/with-markdown-plugins/src/pages/about.astro b/examples/with-markdown-plugins/src/pages/about.astro new file mode 100644 index 000000000..c313f98a9 --- /dev/null +++ b/examples/with-markdown-plugins/src/pages/about.astro @@ -0,0 +1,20 @@ +--- +import { Markdown } from 'astro/components'; +import MainLayout from '../layouts/main.astro' +--- + + + + # About + + Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + + Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + + ## My story + + Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + + Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + + diff --git a/examples/with-markdown-plugins/src/pages/index.md b/examples/with-markdown-plugins/src/pages/index.md new file mode 100644 index 000000000..a6f8879a8 --- /dev/null +++ b/examples/with-markdown-plugins/src/pages/index.md @@ -0,0 +1,34 @@ +--- +layout: ../layouts/main.astro +title: Asto with Remark Plugins +--- + +# Heading 1 + +Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + +## Heading 2 + +Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + +### Heading 3 + +Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + +### Heading 3 + +Sint ullamco sint ut irure laborum occaecat anim minim tempor enim dolore reprehenderit Lorem. Sit qui nisi in quis ut consequat minim. Ad commodo officia nisi culpa proident duis culpa eu reprehenderit incididunt do fugiat proident tempor. Et velit dolor aliqua dolor ipsum. Sunt eiusmod amet enim ut. + +```jsx:file.jsx +import Router from 'next/router' + +function MyComponent() { + const [show, setShow] = useState(false) + + useEffect(() => { + console.log(2) + }, []) + + return <>... +} +``` diff --git a/packages/astro/package.json b/packages/astro/package.json index 28b3b3f56..ebec27310 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -52,7 +52,6 @@ "@babel/generator": "^7.13.9", "@babel/parser": "^7.13.15", "@babel/traverse": "^7.13.15", - "@silvenon/remark-smartypants": "^1.0.0", "@snowpack/plugin-postcss": "^1.4.3", "@snowpack/plugin-sass": "^1.4.0", "acorn": "^7.4.0", @@ -67,14 +66,12 @@ "fast-xml-parser": "^3.19.0", "fdir": "^5.0.0", "find-up": "^5.0.0", - "gray-matter": "^4.0.2", + "unified": "^9.2.1", "gzip-size": "^6.0.0", "hast-to-hyperscript": "~9.0.0", "kleur": "^4.1.4", "locate-character": "^2.0.5", "magic-string": "^0.25.3", - "mdast-util-mdx": "^0.1.1", - "micromark-extension-mdxjs": "^0.3.0", "mime": "^2.5.2", "moize": "^6.0.1", "node-fetch": "^2.6.1", @@ -82,10 +79,6 @@ "postcss": "^8.2.15", "postcss-icss-keyframes": "^0.2.1", "prismjs": "^1.23.0", - "remark-footnotes": "^3.0.0", - "remark-gfm": "^1.0.0", - "remark-parse": "^9.0.0", - "remark-rehype": "^8.1.0", "resolve": "^1.20.0", "rollup": "^2.43.1", "rollup-plugin-terser": "^7.0.2", diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 1fbdd6282..bfb338cdc 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1,4 +1,5 @@ import type { ImportSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier } from '@babel/types'; +import type { AstroMarkdownOptions } from '@astrojs/markdown-support' export interface AstroConfigRaw { dist: string; @@ -9,12 +10,7 @@ export interface AstroConfigRaw { jsx?: string; } -export interface AstroMarkdownOptions { - /** Enable or disable footnotes syntax extension */ - footnotes: boolean; - /** Enable or disable GitHub-flavored Markdown syntax extension */ - gfm: boolean; -} +export { AstroMarkdownOptions } export interface AstroConfig { dist: string; projectRoot: URL; diff --git a/packages/astro/src/compiler/codegen/index.ts b/packages/astro/src/compiler/codegen/index.ts index 0b9780e16..b34077269 100644 --- a/packages/astro/src/compiler/codegen/index.ts +++ b/packages/astro/src/compiler/codegen/index.ts @@ -543,7 +543,6 @@ async function compileHtml(enterNode: TemplateNode, state: CodegenState, compile const { $scope: scopedClassName } = state.markers.insideMarkdown as Record<'$scope', any>; let { content: rendered } = await renderMarkdown(dedent(md), { ...(markdownOptions as AstroMarkdownOptions), - mode: 'astro-md', $: { scopedClassName: scopedClassName && scopedClassName.slice(1, -1) }, }); diff --git a/packages/astro/src/compiler/index.ts b/packages/astro/src/compiler/index.ts index 0f12cc7f0..f612e6165 100644 --- a/packages/astro/src/compiler/index.ts +++ b/packages/astro/src/compiler/index.ts @@ -3,7 +3,7 @@ import type { CompileResult, TransformResult } from '../@types/astro'; import type { CompileOptions } from '../@types/compiler.js'; import path from 'path'; -import { renderMarkdownWithFrontmatter } from '@astrojs/markdown-support'; +import { MarkdownRenderingOptions, renderMarkdownWithFrontmatter } from '@astrojs/markdown-support'; import { parse } from '@astrojs/parser'; import { transform } from './transform/index.js'; @@ -42,12 +42,12 @@ export async function convertAstroToJsx(template: string, opts: ConvertAstroOpti /** * .md -> .astro source */ -export async function convertMdToAstroSource(contents: string, { filename }: { filename: string }): Promise { +export async function convertMdToAstroSource(contents: string, { filename }: { filename: string }, opts?: MarkdownRenderingOptions): Promise { let { content, frontmatter: { layout, ...frontmatter }, ...data - } = await renderMarkdownWithFrontmatter(contents); + } = await renderMarkdownWithFrontmatter(contents, opts); if (frontmatter['astro'] !== undefined) { throw new Error(`"astro" is a reserved word but was used as a frontmatter value!\n\tat ${filename}`); @@ -75,7 +75,7 @@ async function convertMdToJsx( contents: string, { compileOptions, filename, fileID }: { compileOptions: CompileOptions; filename: string; fileID: string } ): Promise { - const raw = await convertMdToAstroSource(contents, { filename }); + const raw = await convertMdToAstroSource(contents, { filename }, compileOptions.astroConfig.markdownOptions); const convertOptions = { compileOptions, filename, fileID }; return await convertAstroToJsx(raw, convertOptions); } diff --git a/packages/astro/test/astro-markdown-plugins.test.js b/packages/astro/test/astro-markdown-plugins.test.js new file mode 100644 index 000000000..44744e5d5 --- /dev/null +++ b/packages/astro/test/astro-markdown-plugins.test.js @@ -0,0 +1,29 @@ +import { suite } from 'uvu'; +import * as assert from 'uvu/assert'; +import { doc } from './test-utils.js'; +import { setup, setupBuild } from './helpers.js'; + +const MarkdownPlugin = suite('Astro Markdown plugin tests'); + +setup(MarkdownPlugin, './fixtures/astro-markdown-plugins'); +setupBuild(MarkdownPlugin, './fixtures/astro-markdown-plugins'); + +MarkdownPlugin('Can render markdown with plugins', async ({ runtime }) => { + const result = await runtime.load('/'); + if (result.error) throw new Error(result.error); + + const $ = doc(result.contents); + assert.equal($('.toc').length, 1, 'Added a TOC'); + assert.ok($('#hello-world').hasClass('title'), 'Added .title to h1'); +}); + +MarkdownPlugin('Can render Astro with plugins', async ({ runtime }) => { + const result = await runtime.load('/astro'); + if (result.error) throw new Error(result.error); + + const $ = doc(result.contents); + assert.equal($('.toc').length, 1, 'Added a TOC'); + assert.ok($('#hello-world').hasClass('title'), 'Added .title to h1'); +}) + +MarkdownPlugin.run(); diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/astro.config.mjs b/packages/astro/test/fixtures/astro-markdown-plugins/astro.config.mjs new file mode 100644 index 000000000..c236c9215 --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown-plugins/astro.config.mjs @@ -0,0 +1,19 @@ +export default { + renderers: [ + '@astrojs/renderer-preact' + ], + markdownOptions: { + remarkPlugins: [ + 'remark-code-titles', + 'remark-slug', + ['remark-autolink-headings', { behavior: 'prepend' }], + ], + rehypePlugins: [ + ['rehype-toc', { headings: ["h2", "h3"] }], + ['rehype-add-classes', { 'h1,h2,h3': 'title', }], + ] + }, + buildOptions: { + sitemap: false, + }, +}; diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/snowpack.config.json b/packages/astro/test/fixtures/astro-markdown-plugins/snowpack.config.json new file mode 100644 index 000000000..8f034781d --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown-plugins/snowpack.config.json @@ -0,0 +1,3 @@ +{ + "workspaceRoot": "../../../../../" +} diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/src/layouts/content.astro b/packages/astro/test/fixtures/astro-markdown-plugins/src/layouts/content.astro new file mode 100644 index 000000000..925a243a9 --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown-plugins/src/layouts/content.astro @@ -0,0 +1,10 @@ + + + + + +
+ +
+ + diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro new file mode 100644 index 000000000..a05a7c3ff --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/astro.astro @@ -0,0 +1,10 @@ +--- +import { Markdown } from 'astro/components'; +import Layout from '../layouts/content.astro'; +--- + + + + # Hello world + + diff --git a/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/index.md b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/index.md new file mode 100644 index 000000000..832ccf0bc --- /dev/null +++ b/packages/astro/test/fixtures/astro-markdown-plugins/src/pages/index.md @@ -0,0 +1,5 @@ +--- +layout: ../layouts/content.astro +--- + +# Hello world diff --git a/packages/markdown-support/package.json b/packages/markdown-support/package.json index 8be2aa4a4..550fef7a7 100644 --- a/packages/markdown-support/package.json +++ b/packages/markdown-support/package.json @@ -2,7 +2,7 @@ "name": "@astrojs/markdown-support", "version": "0.1.2", "main": "./dist/index.js", - "type": "commonjs", + "type": "module", "repository": { "type": "git", "url": "https://github.com/snowpackjs/astro.git", @@ -16,6 +16,18 @@ "build": "astro-scripts build --format cjs \"src/**/*.ts\" && tsc -p tsconfig.json", "dev": "astro-scripts dev \"src/**/*.ts\"" }, + "dependencies": { + "@silvenon/remark-smartypants": "^1.0.0", + "gray-matter": "^4.0.2", + "mdast-util-mdx-expression": "^1.0.0", + "micromark-extension-mdx-expression": "^1.0.0", + "remark-footnotes": "^3.0.0", + "remark-gfm": "^1.0.0", + "remark-parse": "^9.0.0", + "remark-rehype": "^8.1.0", + "unified": "^9.2.1", + "unist-util-map": "^3.0.0" + }, "devDependencies": { "@types/github-slugger": "^1.3.0", "github-slugger": "^1.3.0", diff --git a/packages/markdown-support/src/index.ts b/packages/markdown-support/src/index.ts index 08f171c3c..f311efa7c 100644 --- a/packages/markdown-support/src/index.ts +++ b/packages/markdown-support/src/index.ts @@ -1,62 +1,67 @@ -import type { AstroMarkdownOptions } from './types'; +import type { AstroMarkdownOptions, MarkdownRenderingOptions } from './types'; import createCollectHeaders from './rehype-collect-headers.js'; import scopedStyles from './remark-scoped-styles.js'; -import { remarkCodeBlock, rehypeCodeBlock } from './codeblock.js'; +import remarkExpressions from './remark-expressions.js'; +import rehypeExpressions from './rehype-expressions.js'; +import { rehypeCodeBlock } from './codeblock.js'; +import { loadPlugins } from './load-plugins.js'; import raw from 'rehype-raw'; import unified from 'unified'; import markdown from 'remark-parse'; import markdownToHtml from 'remark-rehype'; -// import smartypants from '@silvenon/remark-smartypants'; import rehypeStringify from 'rehype-stringify'; -export interface MarkdownRenderingOptions extends Partial { - $?: { - scopedClassName: string | null; - }; - mode: 'md' | 'astro-md'; -} +export { AstroMarkdownOptions, MarkdownRenderingOptions }; /** Internal utility for rendering a full markdown file and extracting Frontmatter data */ export async function renderMarkdownWithFrontmatter(contents: string, opts?: MarkdownRenderingOptions | null) { // Dynamic import to ensure that "gray-matter" isn't built by Snowpack const { default: matter } = await import('gray-matter'); const { data: frontmatter, content } = matter(contents); - const value = await renderMarkdown(content, { ...opts, mode: 'md' }); + const value = await renderMarkdown(content, opts); return { ...value, frontmatter }; } /** Shared utility for rendering markdown */ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) { - const { $: { scopedClassName = null } = {}, mode = 'astro-md', footnotes: useFootnotes = true, gfm: useGfm = true } = opts ?? {}; + const { $: { scopedClassName = null } = {}, footnotes: useFootnotes = true, gfm: useGfm = true, remarkPlugins = [], rehypePlugins = [] } = opts ?? {}; const { headers, rehypeCollectHeaders } = createCollectHeaders(); + let parser = unified().use(markdown).use([remarkExpressions, { addResult: true }]); - let parser = unified().use(markdown).use(remarkCodeBlock()); + if (remarkPlugins.length === 0) { + if (useGfm) { + remarkPlugins.push('remark-gfm'); + } + + if (useFootnotes) { + remarkPlugins.push('remark-footnotes'); + } + + remarkPlugins.push('@silvenon/remark-smartypants'); + } + const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins)); + const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins)); + + loadedRemarkPlugins.forEach(([plugin, opts]) => { + parser.use(plugin, opts); + }); if (scopedClassName) { - parser = parser.use(scopedStyles(scopedClassName)); + parser.use(scopedStyles(scopedClassName)); } - if (useGfm) { - const { default: gfm } = await import('remark-gfm'); - parser = parser.use(gfm); - } + parser.use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression'] }); + parser.use(rehypeExpressions); - if (useFootnotes) { - const { default: footnotes } = await import('remark-footnotes'); - parser = parser.use(footnotes); - } + loadedRehypePlugins.forEach(([plugin, opts]) => { + parser.use(plugin, opts); + }); let result: string; try { - const vfile = await parser - .use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw'] }) - .use(raw) - .use(rehypeCollectHeaders) - .use(rehypeCodeBlock()) - .use(rehypeStringify) - .process(content); + const vfile = await parser.use(raw).use(rehypeCollectHeaders).use(rehypeCodeBlock()).use(rehypeStringify, { entities: { useNamedReferences: true }}).process(content); result = vfile.contents.toString(); } catch (err) { throw err; diff --git a/packages/markdown-support/src/load-plugins.ts b/packages/markdown-support/src/load-plugins.ts new file mode 100644 index 000000000..52bc287f8 --- /dev/null +++ b/packages/markdown-support/src/load-plugins.ts @@ -0,0 +1,27 @@ +import unified from "unified"; +import type { Plugin, UnifiedPluginImport } from "./types"; + +async function importPlugin(p: string | UnifiedPluginImport): UnifiedPluginImport { + if (typeof p === 'string') { + return await import(p); + } + + return await p; +} + +export function loadPlugins(items: Plugin[]): Promise<[unified.Plugin] | [unified.Plugin, unified.Settings]>[] { + return items.map((p) => { + return new Promise((resolve, reject) => { + if (Array.isArray(p)) { + const [plugin, opts] = p; + return importPlugin(plugin) + .then((m) => resolve([m.default, opts])) + .catch((e) => reject(e)); + } + + return importPlugin(p) + .then((m) => resolve([m.default])) + .catch((e) => reject(e)); + }); + }); +} diff --git a/packages/markdown-support/src/rehype-collect-headers.ts b/packages/markdown-support/src/rehype-collect-headers.ts index edfcd29bc..de9b78692 100644 --- a/packages/markdown-support/src/rehype-collect-headers.ts +++ b/packages/markdown-support/src/rehype-collect-headers.ts @@ -14,11 +14,13 @@ export default function createCollectHeaders() { depth = Number.parseInt(depth); let text = ''; + visit(node, 'text', (child) => { text += child.value; }); - let slug = slugger.slug(text); + let slug = node.properties.id || slugger.slug(text); + node.properties = node.properties || {}; node.properties.id = slug; headers.push({ depth, slug, text }); diff --git a/packages/markdown-support/src/rehype-expressions.ts b/packages/markdown-support/src/rehype-expressions.ts new file mode 100644 index 000000000..2762f54fc --- /dev/null +++ b/packages/markdown-support/src/rehype-expressions.ts @@ -0,0 +1,12 @@ +import { map } from 'unist-util-map' + +export default function rehypeExpressions(): any { + return function(node: any): any { + return map(node, (child) => { + if (child.type === 'mdxTextExpression') { + return { type: 'text', value: `{${child.value}}` } + } + return child; + }) + } +} diff --git a/packages/markdown-support/src/remark-expressions.ts b/packages/markdown-support/src/remark-expressions.ts new file mode 100644 index 000000000..1cdb37894 --- /dev/null +++ b/packages/markdown-support/src/remark-expressions.ts @@ -0,0 +1,19 @@ +import {mdxExpression} from 'micromark-extension-mdx-expression' +import {mdxExpressionFromMarkdown, mdxExpressionToMarkdown} from 'mdast-util-mdx-expression' + +function remarkExpressions(this: any, options: any) { + let settings = options || {} + let data = this.data() + + add('micromarkExtensions', mdxExpression({})) + add('fromMarkdownExtensions', mdxExpressionFromMarkdown) + add('toMarkdownExtensions', mdxExpressionToMarkdown) + + function add(field: any, value: any) { + /* istanbul ignore if - other extensions. */ + if (data[field]) data[field].push(value) + else data[field] = [value] + } +} + +export default remarkExpressions; diff --git a/packages/markdown-support/src/types.ts b/packages/markdown-support/src/types.ts index b69f7fc28..6df601ae4 100644 --- a/packages/markdown-support/src/types.ts +++ b/packages/markdown-support/src/types.ts @@ -1,6 +1,20 @@ +import unified from 'unified'; + +export type UnifiedPluginImport = Promise<{ default: unified.Plugin }>; +export type Plugin = string | [string, unified.Settings] | UnifiedPluginImport | [UnifiedPluginImport, unified.Settings]; + export interface AstroMarkdownOptions { /** Enable or disable footnotes syntax extension */ footnotes: boolean; /** Enable or disable GitHub-flavored Markdown syntax extension */ gfm: boolean; + remarkPlugins: Plugin[]; + rehypePlugins: Plugin[]; +} + +export interface MarkdownRenderingOptions extends Partial { + /** @internal */ + $?: { + scopedClassName: string | null; + }; } diff --git a/yarn.lock b/yarn.lock index b28fd7b75..5cd35649d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -403,6 +403,11 @@ dependencies: purgecss "^3.1.3" +"@jsdevtools/rehype-toc@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jsdevtools/rehype-toc/-/rehype-toc-3.0.2.tgz#29c32e6b40cd4b5dafd96cb90d5057ac5dab4a51" + integrity sha512-n5JEf16Wr4mdkRMZ8wMP/wN9/sHmTjRPbouXjJH371mZ2LEGDl72t8tEsMRNFerQN/QJtivOxqK1frdGa4QK5Q== + "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz" @@ -1361,6 +1366,13 @@ resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@types/acorn@^4.0.0": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.5.tgz#e29fdf884695e77be4e99e67d748f5147255752d" + integrity sha512-603sPiZ4GVRHPvn6vNgEAvJewKsy+zwRWYS2MeIMemgoAtcjlw2G3lALxrb9OPA17J28bkB71R33yXlQbUatCA== + dependencies: + "@types/estree" "*" + "@types/babel-types@*", "@types/babel-types@^7.0.0": version "7.0.9" resolved "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.9.tgz" @@ -1404,6 +1416,18 @@ resolved "https://registry.npmjs.org/@types/degit/-/degit-2.8.2.tgz" integrity sha512-wUOqW7nZ8AYgm4OKa9i/RD72CrU/X4Vfx2Oez/51qC3RMgPyF3ZUQVLFjV8McV6/ivR7lOlfidRFgGZj7MZe1A== +"@types/estree-jsx@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-0.0.1.tgz#c36d7a1afeb47a95a8ee0b7bc8bc705db38f919d" + integrity sha512-gcLAYiMfQklDCPjQegGn0TBAn9it05ISEsEhlKQUddIk7o2XDokOcTN7HBO8tznM0D9dGezvHEfRZBfZf6me0A== + dependencies: + "@types/estree" "*" + +"@types/estree@*", "@types/estree@^0.0.48": + version "0.0.48" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.48.tgz#18dc8091b285df90db2f25aa7d906cfc394b7f74" + integrity sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew== + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz" @@ -1721,7 +1745,7 @@ acorn-globals@^3.0.0: dependencies: acorn "^4.0.4" -acorn-jsx@^5.0.0, acorn-jsx@^5.3.1: +acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== @@ -1755,11 +1779,6 @@ acorn@^7.0.0, acorn@^7.4.0: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.0: - version "8.2.4" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz" - integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== - add-stream@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz" @@ -2217,7 +2236,7 @@ bluebird@^3.7.2: resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -boolbase@^1.0.0: +boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= @@ -2471,6 +2490,11 @@ camelcase@^2.0.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + camelcase@^4.0.0: version "4.1.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz" @@ -2814,7 +2838,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -comma-separated-tokens@^1.0.0: +comma-separated-tokens@^1.0.0, comma-separated-tokens@^1.0.2: version "1.0.8" resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== @@ -3099,6 +3123,11 @@ css-select@^4.1.2: domutils "^2.6.0" nth-check "^2.0.0" +css-selector-parser@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759" + integrity sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g== + css-unit-converter@^1.1.1: version "1.1.2" resolved "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz" @@ -3927,11 +3956,6 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== -estree-util-is-identifier-name@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-1.1.0.tgz" - integrity sha512-OVJZ3fGGt9By77Ix9NhaRbzfbDV/2rx9EP7YIDJTmsZSEc5kYn2vWcNccYyahJL2uAQZK2a5Or2i0wtIKTPoRQ== - estree-util-value-to-estree@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-1.2.0.tgz" @@ -3939,6 +3963,14 @@ estree-util-value-to-estree@^1.2.0: dependencies: is-plain-obj "^3.0.0" +estree-util-visit@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-1.1.0.tgz#c0ea7942c40ac7889a77b57a11e92f987744bc6f" + integrity sha512-3lXJ4Us9j8TUif9cWcQy81t9p5OLasnDuuhrFiqb+XstmKC1d1LmrQWYsY49/9URcfHE64mPypDBaNK9NwWDPQ== + dependencies: + "@types/estree-jsx" "^0.0.1" + "@types/unist" "^2.0.0" + estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz" @@ -4434,7 +4466,7 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -github-slugger@^1.2.1, github-slugger@^1.3.0: +github-slugger@^1.0.0, github-slugger@^1.2.1, github-slugger@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz" integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== @@ -4720,6 +4752,11 @@ hast-util-from-parse5@^6.0.0: vfile-location "^3.2.0" web-namespaces "^1.0.0" +hast-util-has-property@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hast-util-has-property/-/hast-util-has-property-1.0.4.tgz#9f137565fad6082524b382c1e7d7d33ca5059f36" + integrity sha512-ghHup2voGfgFoHMGnaLHOjbYFACKrRh9KFttdCzMCbFoBMJXiNi2+XTrPP8+q6cDJM/RSqlCfVWrjp1H201rZg== + hast-util-is-element@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz" @@ -4747,6 +4784,23 @@ hast-util-raw@^6.1.0: xtend "^4.0.0" zwitch "^1.0.0" +hast-util-select@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-1.0.1.tgz#9f1591efa62ba0bdf5ea4298b301aaae1dad612d" + integrity sha1-nxWR76YroL316kKYswGqrh2tYS0= + dependencies: + camelcase "^3.0.0" + comma-separated-tokens "^1.0.2" + css-selector-parser "^1.3.0" + hast-util-has-property "^1.0.0" + hast-util-is-element "^1.0.0" + hast-util-whitespace "^1.0.0" + not "^0.1.0" + nth-check "^1.0.1" + property-information "^3.1.0" + space-separated-tokens "^1.1.0" + zwitch "^1.0.0" + hast-util-to-html@^7.1.1: version "7.1.3" resolved "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz" @@ -6235,39 +6289,13 @@ mdast-util-gfm@^0.1.0: mdast-util-gfm-task-list-item "^0.1.0" mdast-util-to-markdown "^0.6.1" -mdast-util-mdx-expression@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-0.1.1.tgz" - integrity sha512-SoO8y1B9NjMOYlNdwXMchuTVvqSTlUmXm1P5QvZNPv7OH7aa8qJV+3aA+vl1DHK9Vk1uZAlgwokjvDQhS6bINA== +mdast-util-mdx-expression@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.0.0.tgz#588449f13c037762c59a5c3dd342c1c0d51f4092" + integrity sha512-lQ6zzJwGt2/smaC3Sv74aJHej1sk9rO8+unfbP69Iq0G/Nbs9gTAzcjTNNXS8P/mdAPlxfA+F/vdxhxWK9ZzJQ== dependencies: - strip-indent "^3.0.0" - -mdast-util-mdx-jsx@~0.1.0: - version "0.1.4" - resolved "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-0.1.4.tgz" - integrity sha512-67KOAvCmypBSpr+AJEAVQg1Obig5Wnguo4ETTxASe5WVP4TLt57bZjDX/9EW5sWYQsO4gPqLxkUOlypVn5rkhg== - dependencies: - mdast-util-to-markdown "^0.6.0" - parse-entities "^2.0.0" - stringify-entities "^3.1.0" - unist-util-remove-position "^3.0.0" - unist-util-stringify-position "^2.0.0" - vfile-message "^2.0.0" - -mdast-util-mdx@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-0.1.1.tgz" - integrity sha512-9nncdnHNYSb4HNxY3AwE6gU632jhbXsDGXe9PkkJoEawYWJ8tTwmEOHGlGa2TCRidtkd6FF5I8ogDU9pTDlQyA== - dependencies: - mdast-util-mdx-expression "~0.1.0" - mdast-util-mdx-jsx "~0.1.0" - mdast-util-mdxjs-esm "~0.1.0" - mdast-util-to-markdown "^0.6.1" - -mdast-util-mdxjs-esm@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-0.1.1.tgz" - integrity sha512-kBiYeashz+nuhfv+712nc4THQhzXIH2gBFUDbuLxuDCqU/fZeg+9FAcdRBx9E13dkpk1p2Xwufzs3wsGJ+mISQ== + "@types/estree-jsx" "^0.0.1" + strip-indent "^4.0.0" mdast-util-to-hast@^10.2.0: version "10.2.0" @@ -6295,6 +6323,11 @@ mdast-util-to-markdown@^0.6.0, mdast-util-to-markdown@^0.6.1, mdast-util-to-mark repeat-string "^1.0.0" zwitch "^1.0.0" +mdast-util-to-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" + integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== + mdast-util-to-string@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz" @@ -6442,50 +6475,77 @@ micromark-extension-gfm@^0.3.0: micromark-extension-gfm-tagfilter "~0.3.0" micromark-extension-gfm-task-list-item "~0.3.0" -micromark-extension-mdx-expression@^0.3.0, micromark-extension-mdx-expression@^0.3.2, micromark-extension-mdx-expression@~0.3.0: - version "0.3.2" - resolved "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-0.3.2.tgz" - integrity sha512-Sh8YHLSAlbm/7TZkVKEC4wDcJE8XhVpZ9hUXBue1TcAicrrzs/oXu7PHH3NcyMemjGyMkiVS34Y0AHC5KG3y4A== +micromark-extension-mdx-expression@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.0.tgz#384758ff635c1942b01c7854a44a51dbfa68e56e" + integrity sha512-a433Der9h4ZCiK7MZhox45Dt6oD0Nm1v2GFt+PQjlgW4Ydt8OTOIgKOaurSXwsy5vp+PohT7W1PUx3Bv4VVcxw== dependencies: - micromark "~2.11.0" - vfile-message "^2.0.0" + micromark-factory-mdx-expression "^1.0.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-events-to-acorn "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" -micromark-extension-mdx-jsx@~0.3.0: - version "0.3.3" - resolved "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-0.3.3.tgz" - integrity sha512-kG3VwaJlzAPdtIVDznfDfBfNGMTIzsHqKpTmMlew/iPnUCDRNkX+48ElpaOzXAtK5axtpFKE3Hu3VBriZDnRTQ== +micromark-factory-mdx-expression@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.0.tgz#b05f2120f9518e95d31093a5918d4ef837003818" + integrity sha512-UbIjQgRdeMdkKt9rkbjyYYJdegccu27ynI7JIHsbNAjtHQGrnsx3bBzJA5NrSjOmzzLaLr2FoVaynR2DwwTCAQ== dependencies: - estree-util-is-identifier-name "^1.0.0" - micromark "~2.11.0" - micromark-extension-mdx-expression "^0.3.2" - vfile-message "^2.0.0" + micromark-factory-whitespace "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-events-to-acorn "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + unist-util-position-from-estree "^1.0.0" + vfile-message "^3.0.0" -micromark-extension-mdx-md@~0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-0.1.1.tgz" - integrity sha512-emlFQEyfx/2aPhwyEqeNDfKE6jPH1cvLTb5ANRo4qZBjaUObnzjLRdzK8RJ4Xc8+/dOmKN8TTRxFnOYF5/EAwQ== - -micromark-extension-mdxjs-esm@~0.3.0: - version "0.3.1" - resolved "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-0.3.1.tgz" - integrity sha512-tuLgcELrgY1a5tPxjk+MrI3BdYtwW67UaHZdzKiDYD8loNbxwIscfdagI6A2BKuAkrfeyHF6FW3B8KuDK3ZMXw== +micromark-factory-space@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz#cebff49968f2b9616c0fcb239e96685cb9497633" + integrity sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew== dependencies: - micromark "~2.11.0" - micromark-extension-mdx-expression "^0.3.0" - vfile-message "^2.0.0" + micromark-util-character "^1.0.0" + micromark-util-types "^1.0.0" -micromark-extension-mdxjs@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-0.3.0.tgz" - integrity sha512-NQuiYA0lw+eFDtSG4+c7ao3RG9dM4P0Kx/sn8OLyPhxtIc6k+9n14k5VfLxRKfAxYRTo8c5PLZPaRNmslGWxJw== +micromark-factory-whitespace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c" + integrity sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A== dependencies: - acorn "^8.0.0" - acorn-jsx "^5.0.0" - micromark "~2.11.0" - micromark-extension-mdx-expression "~0.3.0" - micromark-extension-mdx-jsx "~0.3.0" - micromark-extension-mdx-md "~0.1.0" - micromark-extension-mdxjs-esm "~0.3.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-character@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.0.0.tgz#a6f2f5a9ad4a35a7888223b5fce901f00189f0e8" + integrity sha512-VdfDsHtUn/ocN2hGBkMunHHWcaN33llgwU0bmw2LA0tY1JvVkjHGvdiQSIk0pS3XeGCJLT6syS5i8y+1xbwDnQ== + dependencies: + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-events-to-acorn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.0.0.tgz#b4628ce6cc913888f5b47ae739e4035fd8472ecb" + integrity sha512-1e6kdnEdqIDge9EX/6T/F2QDnV482J+IOhBnTV4MP/yD/ncCfDeWSQQuomRinlgEZxUhJ6tdgOldRVZkrw+qFg== + dependencies: + "@types/acorn" "^4.0.0" + "@types/estree" "^0.0.48" + estree-util-visit "^1.0.0" + micromark-util-types "^1.0.0" + vfile-message "^3.0.0" + +micromark-util-symbol@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.0.0.tgz#91cdbcc9b2a827c0129a177d36241bcd3ccaa34d" + integrity sha512-NZA01jHRNCt4KlOROn8/bGi6vvpEmlXld7EHcRH+aYWUfL3Wc8JLUNNlqUMKa0hhz6GrpUWsHtzPmKof57v0gQ== + +micromark-util-types@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.0.tgz#0ebdfaea3fa7c15fc82b1e06ea1ef0152d0fb2f0" + integrity sha512-psf1WAaP1B77WpW4mBGDkTr+3RsPuDAgsvlP47GJzbH1jmjH8xjOx7Z6kp84L8oqHmy5pYO3Ev46odosZV+3AA== micromark@^2.11.3, micromark@~2.11.0, micromark@~2.11.3: version "2.11.4" @@ -6542,7 +6602,7 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" -min-indent@^1.0.0: +min-indent@^1.0.0, min-indent@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== @@ -6922,6 +6982,11 @@ normalize-url@^4.1.0: resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +not@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/not/-/not-0.1.0.tgz#c9691c1746c55dcfbe54cbd8bd4ff041bc2b519d" + integrity sha1-yWkcF0bFXc++VMvYvU/wQbwrUZ0= + npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz" @@ -7035,6 +7100,13 @@ npmlog@^4.1.2: gauge "~2.7.3" set-blocking "~2.0.0" +nth-check@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + nth-check@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz" @@ -7899,6 +7971,11 @@ promzard@^0.3.0: dependencies: read "1" +property-information@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-3.2.0.tgz#fd1483c8fbac61808f5fe359e7693a1f48a58331" + integrity sha1-/RSDyPusYYCPX+NZ52k6H0ilgzE= + property-information@^5.0.0, property-information@^5.3.0: version "5.6.0" resolved "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz" @@ -8406,6 +8483,13 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" +rehype-add-classes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rehype-add-classes/-/rehype-add-classes-1.0.0.tgz#3d8b37b13ce06e2eb8681b33ccf1c2995611a33b" + integrity sha512-Iz8t2KhCNAL+0AHKjxb+kVwsHk/pI3Cy4k0R70ZGzoQiZ7WQm3o8+3odJkMhFRfcNIK1lNShIHEdC90H5LwLdg== + dependencies: + hast-util-select "^1.0.1" + rehype-parse@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz" @@ -8428,6 +8512,28 @@ rehype-stringify@^8.0.0: dependencies: hast-util-to-html "^7.1.1" +rehype-toc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rehype-toc/-/rehype-toc-3.0.2.tgz#0373e2abafddeb0606ee38229ff6714da6d86d68" + integrity sha512-DMt376+4i1KJGgHJL7Ezd65qKkJ7Eqp6JSB47BJ90ReBrohI9ufrornArM6f4oJjP2E2DVZZHufWucv/9t7GUQ== + dependencies: + "@jsdevtools/rehype-toc" "3.0.2" + +remark-autolink-headings@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/remark-autolink-headings/-/remark-autolink-headings-6.0.1.tgz#074470b8ec7714a0f06fa151e293152bf9723df9" + integrity sha512-LTV5G5NMjypHEr14tMNJ36yrP+xwT7mejJelZOPXKiF5WvRH9o36zXnr2QGqfms2yVASNpDaC9NBOwKlJJKuQw== + dependencies: + extend "^3.0.0" + unist-util-visit "^2.0.0" + +remark-code-titles@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/remark-code-titles/-/remark-code-titles-0.1.2.tgz#ae41b47c517eae4084c761a59a60df5f0bd54aa8" + integrity sha512-KsHQbaI4FX8Ozxqk7YErxwmBiveUqloKuVqyPG2YPLHojpgomodWgRfG4B+bOtmn/5bfJ8khw4rR0lvgVFl2Uw== + dependencies: + unist-util-visit "^1.4.0" + remark-footnotes@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-3.0.0.tgz" @@ -8458,6 +8564,15 @@ remark-rehype@^8.1.0: dependencies: mdast-util-to-hast "^10.2.0" +remark-slug@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.0.0.tgz#2b54a14a7b50407a5e462ac2f376022cce263e2c" + integrity sha512-ln67v5BrGKHpETnm6z6adlJPhESFJwfuZZ3jrmi+lKTzeZxh2tzFzUfDD4Pm2hRGOarHLuGToO86MNMZ/hA67Q== + dependencies: + github-slugger "^1.0.0" + mdast-util-to-string "^1.0.0" + unist-util-visit "^2.0.0" + repeat-string@^1.0.0, repeat-string@^1.5.2: version "1.6.1" resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" @@ -9109,7 +9224,7 @@ sourcemap-codec@^1.4.4: resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -space-separated-tokens@^1.0.0: +space-separated-tokens@^1.0.0, space-separated-tokens@^1.1.0: version "1.1.5" resolved "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== @@ -9315,7 +9430,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^3.0.1, stringify-entities@^3.1.0: +stringify-entities@^3.0.1: version "3.1.0" resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.1.0.tgz" integrity sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg== @@ -9410,6 +9525,13 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-4.0.0.tgz#b41379433dd06f5eae805e21d631e07ee670d853" + integrity sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA== + dependencies: + min-indent "^1.0.1" + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" @@ -9983,6 +10105,11 @@ unist-util-generated@^1.0.0: resolved "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz" integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== +unist-util-is@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" + integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== + unist-util-is@^4.0.0: version "4.1.0" resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz" @@ -9993,6 +10120,13 @@ unist-util-is@^5.0.0: resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.1.0.tgz" integrity sha512-pWspZ+AvTqYbC+xWeRmzGqbcY8Na08Eowlfs2xchWTYot8vBBAq+syrE/LWS0bw1D/JOu4lwzDbEb6Mz13tK+g== +unist-util-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-map/-/unist-util-map-3.0.0.tgz#ec4c3e4f3f65f559b6c232087af2a470f3e5db89" + integrity sha512-kyPbOAlOPZpytdyquF1g6qYpAjkpMpSPtR7TAj4SOQWSJfQ/LN+IFI2oWBvkxzhsPKxiMKZcgpp5ihZLLvNl6g== + dependencies: + "@types/unist" "^2.0.0" + unist-util-modify-children@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-2.0.0.tgz" @@ -10000,18 +10134,18 @@ unist-util-modify-children@^2.0.0: dependencies: array-iterate "^1.0.0" +unist-util-position-from-estree@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.0.tgz#31f5e0f771629ec718443765e10ea590231760c7" + integrity sha512-FveBeLp2Oc5XTn8DCX++DB4ETP8BaiRl0DuVGdD8s5I+V/boj81rBm0pNdwAixXUtGvVdsinPOT+Thy2yFrMEA== + dependencies: + "@types/unist" "^2.0.0" + unist-util-position@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz" integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== -unist-util-remove-position@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-3.0.0.tgz" - integrity sha512-17kIOuolVuK16LMb9KyMJlqdfCtlfQY5FjY3Sdo9iC7F5wqdXhNjMq0PBvMpkVNNnAmHxXssUW+rZ9T2zbP0Rg== - dependencies: - unist-util-visit "^2.0.0" - unist-util-stringify-position@^2.0.0: version "2.0.3" resolved "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz" @@ -10019,11 +10153,25 @@ unist-util-stringify-position@^2.0.0: dependencies: "@types/unist" "^2.0.2" +unist-util-stringify-position@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.0.tgz#d517d2883d74d0daa0b565adc3d10a02b4a8cde9" + integrity sha512-SdfAl8fsDclywZpfMDTVDxA2V7LjtRDTOFd44wUJamgl6OlVngsqWjxvermMYf60elWHbxhuRCZml7AnuXCaSA== + dependencies: + "@types/unist" "^2.0.0" + unist-util-visit-children@^1.0.0: version "1.1.4" resolved "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-1.1.4.tgz" integrity sha512-sA/nXwYRCQVRwZU2/tQWUqJ9JSFM1X3x7JIOsIgSzrFHcfVt6NkzDtKzyxg2cZWkCwGF9CO8x4QNZRJRMK8FeQ== +unist-util-visit-parents@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" + integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== + dependencies: + unist-util-is "^3.0.0" + unist-util-visit-parents@^3.0.0: version "3.1.1" resolved "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz" @@ -10040,6 +10188,13 @@ unist-util-visit-parents@^4.0.0: "@types/unist" "^2.0.0" unist-util-is "^5.0.0" +unist-util-visit@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" + integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== + dependencies: + unist-util-visit-parents "^2.0.0" + unist-util-visit@^2.0.0, unist-util-visit@^2.0.1: version "2.0.3" resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz" @@ -10203,6 +10358,14 @@ vfile-message@^2.0.0: "@types/unist" "^2.0.0" unist-util-stringify-position "^2.0.0" +vfile-message@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.0.1.tgz#b9bcf87cb5525e61777e0c6df07e816a577588a3" + integrity sha512-gYmSHcZZUEtYpTmaWaFJwsuUD70/rTY4v09COp8TGtOkix6gGxb/a8iTQByIY9ciTk9GwAwIXd/J9OPfM4Bvaw== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^3.0.0" + vfile@^4.0.0: version "4.2.1" resolved "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz"