diff --git a/jest/deps.d.ts b/jest/deps.d.ts index 16ad1b75fdbd..965ca69039cc 100644 --- a/jest/deps.d.ts +++ b/jest/deps.d.ts @@ -20,6 +20,10 @@ declare module 'remark-mdx' { export = mdx; } +declare module 'remark-rehype'; + +declare module 'rehype-stringify'; + declare module '@testing-utils/git' { const createTempRepo: typeof import('./utils/git').createTempRepo; export {createTempRepo}; diff --git a/packages/docusaurus-mdx-loader/package.json b/packages/docusaurus-mdx-loader/package.json index 1efcee5eaf0e..c787a0ebe11a 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -44,6 +44,8 @@ "@types/unist": "^2.0.6", "remark": "^12.0.1", "remark-mdx": "^1.6.21", + "rehype-stringify": "^8.0.0", + "remark-rehype": "^8.1.0", "to-vfile": "^6.1.0", "unist-builder": "^2.0.3", "unist-util-remove-position": "^3.0.0" diff --git a/packages/docusaurus-mdx-loader/src/loader.ts b/packages/docusaurus-mdx-loader/src/loader.ts index 638a62566724..764d62c2bdc5 100644 --- a/packages/docusaurus-mdx-loader/src/loader.ts +++ b/packages/docusaurus-mdx-loader/src/loader.ts @@ -23,8 +23,10 @@ import unwrapMdxCodeBlocks from './remark/unwrapMdxCodeBlocks'; import transformImage from './remark/transformImage'; import transformLinks from './remark/transformLinks'; +import transformAdmonitions from './remark/admonitions'; import type {LoaderContext} from 'webpack'; import type {Processor, Plugin} from 'unified'; +import type {AdmonitionOptions} from './remark/admonitions'; const { loaders: {inlineMarkdownImageFileLoader}, @@ -37,6 +39,7 @@ const pragma = ` `; const DEFAULT_OPTIONS: MDXOptions = { + admonitions: true, rehypePlugins: [], remarkPlugins: [unwrapMdxCodeBlocks, emoji, headings, toc], beforeDefaultRemarkPlugins: [], @@ -48,7 +51,9 @@ const compilerCache = new Map(); export type MDXPlugin = // eslint-disable-next-line @typescript-eslint/no-explicit-any [Plugin, any] | Plugin; + export type MDXOptions = { + admonitions: boolean | AdmonitionOptions; remarkPlugins: MDXPlugin[]; rehypePlugins: MDXPlugin[]; beforeDefaultRemarkPlugins: MDXPlugin[]; @@ -132,6 +137,19 @@ function createAssetsExportCode(assets: unknown) { return `{\n${codeLines.join('\n')}\n}`; } +function getAdmonitionsPlugins( + admonitionsOption: MDXOptions['admonitions'], +): MDXPlugin[] { + if (admonitionsOption) { + const plugin: MDXPlugin = + admonitionsOption === true + ? transformAdmonitions + : [transformAdmonitions, admonitionsOption]; + return [plugin]; + } + return []; +} + export async function mdxLoader( this: LoaderContext, fileString: string, @@ -149,32 +167,37 @@ export async function mdxLoader( const hasFrontMatter = Object.keys(frontMatter).length > 0; if (!compilerCache.has(this.query)) { - const options: Options = { - ...reqOptions, - remarkPlugins: [ - ...(reqOptions.beforeDefaultRemarkPlugins ?? []), - ...DEFAULT_OPTIONS.remarkPlugins, - [ - transformImage, - { - staticDirs: reqOptions.staticDirs, - siteDir: reqOptions.siteDir, - }, - ], - [ - transformLinks, - { - staticDirs: reqOptions.staticDirs, - siteDir: reqOptions.siteDir, - }, - ], - ...(reqOptions.remarkPlugins ?? []), + const remarkPlugins: MDXPlugin[] = [ + ...(reqOptions.beforeDefaultRemarkPlugins ?? []), + ...getAdmonitionsPlugins(reqOptions.admonitions ?? false), + ...DEFAULT_OPTIONS.remarkPlugins, + [ + transformImage, + { + staticDirs: reqOptions.staticDirs, + siteDir: reqOptions.siteDir, + }, ], - rehypePlugins: [ - ...(reqOptions.beforeDefaultRehypePlugins ?? []), - ...DEFAULT_OPTIONS.rehypePlugins, - ...(reqOptions.rehypePlugins ?? []), + [ + transformLinks, + { + staticDirs: reqOptions.staticDirs, + siteDir: reqOptions.siteDir, + }, ], + ...(reqOptions.remarkPlugins ?? []), + ]; + + const rehypePlugins: MDXPlugin[] = [ + ...(reqOptions.beforeDefaultRehypePlugins ?? []), + ...DEFAULT_OPTIONS.rehypePlugins, + ...(reqOptions.rehypePlugins ?? []), + ]; + + const options: Options = { + ...reqOptions, + remarkPlugins, + rehypePlugins, }; compilerCache.set(this.query, [createCompiler(options), options]); } diff --git a/packages/docusaurus-mdx-loader/src/remark/admonitions/LICENSE b/packages/docusaurus-mdx-loader/src/remark/admonitions/LICENSE new file mode 100644 index 000000000000..faa2221713df --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/admonitions/LICENSE @@ -0,0 +1,45 @@ +MIT License + +Copyright (c) 2020 Elvis Wolcott + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +MIT License + +Copyright (c) Facebook, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/docusaurus-mdx-loader/src/remark/admonitions/README.md b/packages/docusaurus-mdx-loader/src/remark/admonitions/README.md new file mode 100644 index 000000000000..3add9f4f32a4 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/admonitions/README.md @@ -0,0 +1,3 @@ +# Docusaurus admonitions + +Code from [remark-admonitions](https://github.com/remarkjs/remark-directive) (MIT license) has been copied to this folder, and highly customized for Docusaurus needs. diff --git a/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/base.md b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/base.md new file mode 100644 index 000000000000..a9d3e4159661 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/base.md @@ -0,0 +1,25 @@ +The blog feature enables you to deploy in no time a full-featured blog. + +:::info Sample Title + +Check the [Blog Plugin API Reference documentation](./api/plugins/plugin-content-blog.md) for an exhaustive list of options. + +::: + +## Initial setup {#initial-setup} + +To set up your site's blog, start by creating a `blog` directory. + +:::tip + +Use the **[Fast Track](introduction.md#fast-track)** to understand Docusaurus in **5 minutes ⏱**! + +Use **[docusaurus.new](https://docusaurus.new)** to test Docusaurus immediately in your browser! + +::: + +++++tip + +Admonition with different syntax + +++++ diff --git a/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/interpolation.md b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/interpolation.md new file mode 100644 index 000000000000..a20e62976bf7 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__fixtures__/interpolation.md @@ -0,0 +1,7 @@ +Test admonition with interpolated title/body + +:::tip My `interpolated` **title** + +`body` **interpolated** + +::: diff --git a/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000000..227eb28b3370 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/__snapshots__/index.test.ts.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`admonitions remark plugin base 1`] = ` +"

The blog feature enables you to deploy in no time a full-featured blog.

+

Check the Blog Plugin API Reference documentation for an exhaustive list of options.

+

Initial setup {#initial-setup}

+

To set up your site's blog, start by creating a blog directory.

+

Use the Fast Track to understand Docusaurus in 5 minutes ⏱!

Use docusaurus.new to test Docusaurus immediately in your browser!

+

++++tip

+

Admonition with different syntax

+

++++

" +`; + +exports[`admonitions remark plugin custom keywords 1`] = ` +"

The blog feature enables you to deploy in no time a full-featured blog.

+

:::info Sample Title

+

Check the Blog Plugin API Reference documentation for an exhaustive list of options.

+

:::

+

Initial setup {#initial-setup}

+

To set up your site's blog, start by creating a blog directory.

+

Use the Fast Track to understand Docusaurus in 5 minutes ⏱!

Use docusaurus.new to test Docusaurus immediately in your browser!

+

++++tip

+

Admonition with different syntax

+

++++

" +`; + +exports[`admonitions remark plugin custom tag 1`] = ` +"

The blog feature enables you to deploy in no time a full-featured blog.

+

:::info Sample Title

+

Check the Blog Plugin API Reference documentation for an exhaustive list of options.

+

:::

+

Initial setup {#initial-setup}

+

To set up your site's blog, start by creating a blog directory.

+

:::tip

+

Use the Fast Track to understand Docusaurus in 5 minutes ⏱!

+

Use docusaurus.new to test Docusaurus immediately in your browser!

+

:::

+

Admonition with different syntax

" +`; + +exports[`admonitions remark plugin interpolation 1`] = ` +"

Test admonition with interpolated title/body

+My interpolated title <button style={{color: "red"}} onClick={() => alert("click")}>test

body interpolated content

" +`; diff --git a/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/index.test.ts b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/index.test.ts new file mode 100644 index 000000000000..3794562bb16d --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/admonitions/__tests__/index.test.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import path from 'path'; +import remark from 'remark'; +import remark2rehype from 'remark-rehype'; +import stringify from 'rehype-stringify'; + +import vfile from 'to-vfile'; +import plugin from '../index'; +import type {AdmonitionOptions} from '../index'; + +const processFixture = async ( + name: string, + options?: Partial, +) => { + const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); + const file = await vfile.read(filePath); + + const result = await remark() + .use(plugin, options) + .use(remark2rehype) + .use(stringify) + .process(file); + + return result.toString(); +}; + +describe('admonitions remark plugin', () => { + it('base', async () => { + const result = await processFixture('base'); + expect(result).toMatchSnapshot(); + }); + + it('custom keywords', async () => { + const result = await processFixture('base', {keywords: ['tip']}); + expect(result).toMatchSnapshot(); + }); + + it('custom tag', async () => { + const result = await processFixture('base', {tag: '++++'}); + expect(result).toMatchSnapshot(); + }); + + it('interpolation', async () => { + const result = await processFixture('interpolation'); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/packages/docusaurus-mdx-loader/src/remark/admonitions/index.ts b/packages/docusaurus-mdx-loader/src/remark/admonitions/index.ts new file mode 100644 index 000000000000..d423e6e793ea --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/remark/admonitions/index.ts @@ -0,0 +1,182 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import visit from 'unist-util-visit'; +import type {Transformer, Processor, Plugin} from 'unified'; +import type {Literal} from 'mdast'; + +const NEWLINE = '\n'; + +export type AdmonitionOptions = { + tag: string; + keywords: string[]; +}; + +export const DefaultAdmonitionOptions: AdmonitionOptions = { + tag: ':::', + keywords: [ + 'secondary', + 'info', + 'success', + 'danger', + 'note', + 'tip', + 'warning', + 'important', + 'caution', + ], +}; + +function escapeRegExp(s: string): string { + return s.replace(/[-[\]{}()*+?.\\^$|/]/g, '\\$&'); +} + +function normalizeOptions( + options: Partial, +): AdmonitionOptions { + return {...DefaultAdmonitionOptions, ...options}; +} + +// This string value does not matter much +// It is ignored because nodes are using hName/hProperties coming from HAST +const admonitionNodeType = 'admonitionHTML'; + +const plugin: Plugin = function plugin( + this: Processor, + optionsInput: Partial = {}, +): Transformer { + const options = normalizeOptions(optionsInput); + + const keywords = Object.values(options.keywords).map(escapeRegExp).join('|'); + const tag = escapeRegExp(options.tag); + const regex = new RegExp(`${tag}(${keywords})(?: *(.*))?\n`); + const escapeTag = new RegExp(escapeRegExp(`\\${options.tag}`), 'g'); + + // The tokenizer is called on blocks to determine if there is an admonition + // present and create tags for it + function blockTokenizer(this: any, eat: any, value: string, silent: boolean) { + // Stop if no match or match does not start at beginning of line + const match = regex.exec(value); + if (!match || match.index !== 0) { + return false; + } + // If silent return the match + if (silent) { + return true; + } + + const now = eat.now(); + const [opening, keyword, title] = match; + const food = []; + const content = []; + + let newValue = value; + // consume lines until a closing tag + let idx = newValue.indexOf(NEWLINE); + while (idx !== -1) { + // grab this line and eat it + const next = newValue.indexOf(NEWLINE, idx + 1); + const line = + next !== -1 ? newValue.slice(idx + 1, next) : newValue.slice(idx + 1); + food.push(line); + newValue = newValue.slice(idx + 1); + // the closing tag is NOT part of the content + if (line.startsWith(options.tag)) { + break; + } + content.push(line); + idx = newValue.indexOf(NEWLINE); + } + + // consume the processed tag and replace escape sequences + const contentString = content.join(NEWLINE).replace(escapeTag, options.tag); + const add = eat(opening + food.join(NEWLINE)); + + // parse the content in block mode + const exit = this.enterBlock(); + const contentNodes = this.tokenizeBlock(contentString, now); + exit(); + + const titleNodes = this.tokenizeInline(title, now); + + const isSimpleTextTitle = + titleNodes.length === 1 && titleNodes[0].type === 'text'; + + const element = { + type: admonitionNodeType, + data: { + // hName/hProperties come from HAST + // See https://github.com/syntax-tree/mdast-util-to-hast#fields-on-nodes + hName: 'admonition', + hProperties: { + ...(title && isSimpleTextTitle && {title}), + type: keyword, + }, + }, + children: [ + // For titles containing MDX syntax: create a custom element. The theme + // component will extract it and render it nicely. + // + // Temporary workaround, because it's complex in MDX v1 to emit + // interpolated JSX prop syntax (title={<>my title}). + // For this reason, we use children instead of the title prop. + title && + !isSimpleTextTitle && { + type: admonitionNodeType, + data: { + hName: 'mdxAdmonitionTitle', + hProperties: {}, + }, + children: titleNodes, + }, + ...contentNodes, + ].filter(Boolean), + }; + + return add(element); + } + + // add tokenizer to parser after fenced code blocks + const Parser = this.Parser.prototype; + Parser.blockTokenizers.admonition = blockTokenizer; + Parser.blockMethods.splice( + Parser.blockMethods.indexOf('fencedCode') + 1, + 0, + 'admonition', + ); + Parser.interruptParagraph.splice( + Parser.interruptParagraph.indexOf('fencedCode') + 1, + 0, + ['admonition'], + ); + Parser.interruptList.splice( + Parser.interruptList.indexOf('fencedCode') + 1, + 0, + ['admonition'], + ); + Parser.interruptBlockquote.splice( + Parser.interruptBlockquote.indexOf('fencedCode') + 1, + 0, + ['admonition'], + ); + + return (root) => { + // escape everything except admonitionHTML nodes + visit( + root, + (node: unknown): node is Literal => + (node as Literal)?.type !== admonitionNodeType, + (node: Literal) => { + if (node.value) { + node.value = node.value.replace(escapeTag, options.tag); + } + }, + ); + }; +}; + +export default plugin; diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index f5bfcff31857..c8e65adfe958 100644 --- a/packages/docusaurus-plugin-content-blog/package.json +++ b/packages/docusaurus-plugin-content-blog/package.json @@ -30,7 +30,6 @@ "fs-extra": "^10.1.0", "lodash": "^4.17.21", "reading-time": "^1.5.0", - "remark-admonitions": "^1.2.1", "tslib": "^2.4.0", "unist-util-visit": "^2.0.3", "utility-types": "^3.10.0", diff --git a/packages/docusaurus-plugin-content-blog/src/deps.d.ts b/packages/docusaurus-plugin-content-blog/src/deps.d.ts deleted file mode 100644 index 6b8b33906b54..000000000000 --- a/packages/docusaurus-plugin-content-blog/src/deps.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -declare module 'remark-admonitions' { - type Options = {[key: string]: unknown}; - - const plugin: (options?: Options) => void; - export = plugin; -} diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index f0c1f8cc24ed..c4073e49274d 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -21,7 +21,6 @@ import { type TagsListItem, type TagModule, } from '@docusaurus/utils'; -import admonitions from 'remark-admonitions'; import { generateBlogPosts, getSourceToPermalink, @@ -49,12 +48,6 @@ export default async function pluginContentBlog( context: LoadContext, options: PluginOptions, ): Promise> { - if (options.admonitions) { - options.remarkPlugins = options.remarkPlugins.concat([ - [admonitions, options.admonitions], - ]); - } - const { siteDir, siteConfig, @@ -381,6 +374,7 @@ export default async function pluginContentBlog( configureWebpack(_config, isServer, {getJSLoader}, content) { const { + admonitions, rehypePlugins, remarkPlugins, truncateMarker, @@ -423,6 +417,7 @@ export default async function pluginContentBlog( { loader: require.resolve('@docusaurus/mdx-loader'), options: { + admonitions, remarkPlugins, rehypePlugins, beforeDefaultRemarkPlugins: [ diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts index 0375aba578e3..99c4067a9ad8 100644 --- a/packages/docusaurus-plugin-content-blog/src/options.ts +++ b/packages/docusaurus-plugin-content-blog/src/options.ts @@ -24,7 +24,7 @@ export const DEFAULT_OPTIONS: PluginOptions = { feedOptions: {type: ['rss', 'atom'], copyright: ''}, beforeDefaultRehypePlugins: [], beforeDefaultRemarkPlugins: [], - admonitions: {}, + admonitions: true, truncateMarker: //, rehypePlugins: [], remarkPlugins: [], diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index e8a8697fa8cd..ae87a218105b 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -377,7 +377,6 @@ declare module '@docusaurus/plugin-content-blog' { * unlocalized file. Ignored when `editUrl` is a function. */ editLocalizedFiles?: boolean; - admonitions: {[key: string]: unknown}; /** Path to the authors map file, relative to the blog content directory. */ authorsMapPath: string; /** A callback to customize the reading time number displayed. */ diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index bc6a154f51b4..62ec60d02af6 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -38,7 +38,6 @@ "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", - "remark-admonitions": "^1.2.1", "tslib": "^2.4.0", "utility-types": "^3.10.0", "webpack": "^5.72.1" diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts index 534eb3712000..69ce0b9a597e 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts @@ -63,7 +63,7 @@ describe('normalizeDocsPluginOptions', () => { breadcrumbs: true, showLastUpdateTime: true, showLastUpdateAuthor: true, - admonitions: {}, + admonitions: false, includeCurrentVersion: false, disableVersioning: true, editCurrentVersion: true, @@ -84,7 +84,6 @@ describe('normalizeDocsPluginOptions', () => { expect(testValidate(userOptions)).toEqual({ ...defaultOptions, ...userOptions, - remarkPlugins: [...userOptions.remarkPlugins!, expect.any(Array)], }); }); @@ -102,7 +101,6 @@ describe('normalizeDocsPluginOptions', () => { expect(testValidate(userOptions)).toEqual({ ...defaultOptions, ...userOptions, - remarkPlugins: [...userOptions.remarkPlugins!, expect.any(Array)], }); }); @@ -116,14 +114,14 @@ describe('normalizeDocsPluginOptions', () => { }); }); - it('rejects admonitions true', () => { - const admonitionsTrue: Options = { - admonitions: true, - }; + it('rejects admonitions array', () => { expect(() => - testValidate(admonitionsTrue), + testValidate({ + // @ts-expect-error: rejected value + admonitions: [], + }), ).toThrowErrorMatchingInlineSnapshot( - `""admonitions" contains an invalid value"`, + `""admonitions" does not look like a valid admonitions config"`, ); }); diff --git a/packages/docusaurus-plugin-content-docs/src/deps.d.ts b/packages/docusaurus-plugin-content-docs/src/deps.d.ts deleted file mode 100644 index 6b8b33906b54..000000000000 --- a/packages/docusaurus-plugin-content-docs/src/deps.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -declare module 'remark-admonitions' { - type Options = {[key: string]: unknown}; - - const plugin: (options?: Options) => void; - export = plugin; -} diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index a8557542430f..c9ce6d8decd0 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -352,6 +352,7 @@ export default async function pluginContentDocs( { loader: require.resolve('@docusaurus/mdx-loader'), options: { + admonitions: options.admonitions, remarkPlugins, rehypePlugins, beforeDefaultRehypePlugins, diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index bd1cd5d70732..f81479baff7a 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -14,7 +14,6 @@ import { URISchema, } from '@docusaurus/utils-validation'; import {GlobExcludeDefault} from '@docusaurus/utils'; -import admonitions from 'remark-admonitions'; import {DefaultSidebarItemsGenerator} from './sidebars/generator'; import { DefaultNumberPrefixParser, @@ -42,7 +41,7 @@ export const DEFAULT_OPTIONS: Omit = { beforeDefaultRehypePlugins: [], showLastUpdateTime: false, showLastUpdateAuthor: false, - admonitions: {}, + admonitions: true, includeCurrentVersion: true, disableVersioning: false, lastVersion: undefined, @@ -123,9 +122,7 @@ const OptionsSchema = Joi.object({ beforeDefaultRehypePlugins: RehypePluginsSchema.default( DEFAULT_OPTIONS.beforeDefaultRehypePlugins, ), - admonitions: Joi.alternatives() - .try(AdmonitionsSchema, Joi.boolean().invalid(true)) - .default(DEFAULT_OPTIONS.admonitions), + admonitions: AdmonitionsSchema.default(DEFAULT_OPTIONS.admonitions), showLastUpdateTime: Joi.bool().default(DEFAULT_OPTIONS.showLastUpdateTime), showLastUpdateAuthor: Joi.bool().default( DEFAULT_OPTIONS.showLastUpdateAuthor, @@ -167,11 +164,5 @@ export function validateOptions({ const normalizedOptions = validate(OptionsSchema, options); - if (normalizedOptions.admonitions) { - normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([ - [admonitions, normalizedOptions.admonitions], - ]); - } - return normalizedOptions; } diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index 835c87204375..a96efd1b804d 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -206,7 +206,6 @@ declare module '@docusaurus/plugin-content-docs' { docTagsListComponent: string; /** Root component of the generated category index page. */ docCategoryGeneratedIndexComponent: string; - admonitions: {[key: string]: unknown}; sidebarItemsGenerator: import('./sidebars/types').SidebarItemsGeneratorOption; /** * URL route for the tags section of your doc version. Will be appended to diff --git a/packages/docusaurus-plugin-content-pages/package.json b/packages/docusaurus-plugin-content-pages/package.json index c9167e386d3a..be36197882aa 100644 --- a/packages/docusaurus-plugin-content-pages/package.json +++ b/packages/docusaurus-plugin-content-pages/package.json @@ -24,7 +24,6 @@ "@docusaurus/utils": "2.0.0-beta.21", "@docusaurus/utils-validation": "2.0.0-beta.21", "fs-extra": "^10.1.0", - "remark-admonitions": "^1.2.1", "tslib": "^2.4.0", "webpack": "^5.72.1" }, diff --git a/packages/docusaurus-plugin-content-pages/src/deps.d.ts b/packages/docusaurus-plugin-content-pages/src/deps.d.ts deleted file mode 100644 index 6b8b33906b54..000000000000 --- a/packages/docusaurus-plugin-content-pages/src/deps.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -declare module 'remark-admonitions' { - type Options = {[key: string]: unknown}; - - const plugin: (options?: Options) => void; - export = plugin; -} diff --git a/packages/docusaurus-plugin-content-pages/src/index.ts b/packages/docusaurus-plugin-content-pages/src/index.ts index 7480962d07cc..bf6b0c73041b 100644 --- a/packages/docusaurus-plugin-content-pages/src/index.ts +++ b/packages/docusaurus-plugin-content-pages/src/index.ts @@ -21,7 +21,6 @@ import { DEFAULT_PLUGIN_ID, parseMarkdownString, } from '@docusaurus/utils'; -import admonitions from 'remark-admonitions'; import {validatePageFrontMatter} from './frontMatter'; import type {LoadContext, Plugin} from '@docusaurus/types'; @@ -43,11 +42,6 @@ export default function pluginContentPages( context: LoadContext, options: PluginOptions, ): Plugin { - if (options.admonitions) { - options.remarkPlugins = options.remarkPlugins.concat([ - [admonitions, options.admonitions], - ]); - } const {siteConfig, siteDir, generatedFilesDir, localizationDir} = context; const contentPaths: PagesContentPaths = { @@ -170,6 +164,7 @@ export default function pluginContentPages( configureWebpack(config, isServer, {getJSLoader}) { const { + admonitions, rehypePlugins, remarkPlugins, beforeDefaultRehypePlugins, @@ -194,6 +189,7 @@ export default function pluginContentPages( { loader: require.resolve('@docusaurus/mdx-loader'), options: { + admonitions, remarkPlugins, rehypePlugins, beforeDefaultRehypePlugins, diff --git a/packages/docusaurus-plugin-content-pages/src/options.ts b/packages/docusaurus-plugin-content-pages/src/options.ts index 2c1f38201c30..cce458f71d45 100644 --- a/packages/docusaurus-plugin-content-pages/src/options.ts +++ b/packages/docusaurus-plugin-content-pages/src/options.ts @@ -25,7 +25,7 @@ export const DEFAULT_OPTIONS: PluginOptions = { rehypePlugins: [], beforeDefaultRehypePlugins: [], beforeDefaultRemarkPlugins: [], - admonitions: {}, + admonitions: true, }; const PluginOptionSchema = Joi.object({ diff --git a/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts b/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts index dd754b175940..f60623c7de65 100644 --- a/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts +++ b/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts @@ -16,7 +16,6 @@ declare module '@docusaurus/plugin-content-pages' { include: string[]; exclude: string[]; mdxPageComponent: string; - admonitions: {[key: string]: unknown}; }; export type Options = Partial; diff --git a/packages/docusaurus-theme-classic/src/index.ts b/packages/docusaurus-theme-classic/src/index.ts index b52f14d98b57..d005e7e6e731 100644 --- a/packages/docusaurus-theme-classic/src/index.ts +++ b/packages/docusaurus-theme-classic/src/index.ts @@ -139,7 +139,6 @@ export default function themeClassic( const modules = [ require.resolve(getInfimaCSSFile(direction)), './prism-include-languages', - './admonitions.css', './nprogress', ]; diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index f182ba6470ac..69be740d60ae 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -42,7 +42,7 @@ declare module '@theme/Admonition' { readonly children: ReactNode; readonly type: 'note' | 'tip' | 'danger' | 'info' | 'caution'; readonly icon?: ReactNode; - readonly title?: string; + readonly title?: ReactNode; } export default function Admonition(props: Props): JSX.Element; } @@ -675,6 +675,7 @@ declare module '@theme/MDXComponents' { import type MDXDetails from '@theme/MDXComponents/Details'; import type MDXUl from '@theme/MDXComponents/Ul'; import type MDXImg from '@theme/MDXComponents/Img'; + import type Admonition from '@theme/Admonition'; export type MDXComponentsObject = { readonly head: typeof MDXHead; @@ -690,6 +691,7 @@ declare module '@theme/MDXComponents' { readonly h4: (props: ComponentProps<'h4'>) => JSX.Element; readonly h5: (props: ComponentProps<'h5'>) => JSX.Element; readonly h6: (props: ComponentProps<'h6'>) => JSX.Element; + readonly admonition: typeof Admonition; // eslint-disable-next-line @typescript-eslint/no-explicit-any [tagName: string]: ComponentType; }; diff --git a/packages/docusaurus-theme-classic/src/theme/Admonition/index.tsx b/packages/docusaurus-theme-classic/src/theme/Admonition/index.tsx index 045effa48e98..0d4fe1662139 100644 --- a/packages/docusaurus-theme-classic/src/theme/Admonition/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Admonition/index.tsx @@ -5,102 +5,161 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {type ReactNode} from 'react'; import clsx from 'clsx'; import type {Props} from '@theme/Admonition'; -const icons = { - note: ( - +import styles from './styles.module.css'; + +function NoteIcon() { + return ( + - ), - tip: ( - + ); +} + +function TipIcon() { + return ( + - ), - danger: ( - + ); +} + +function DangerIcon() { + return ( + - ), - info: ( - + ); +} + +function InfoIcon() { + return ( + - ), - caution: ( - + ); +} + +function CautionIcon() { + return ( + - ), + ); +} + +type AdmonitionConfig = { + iconComponent: React.ComponentType; + infimaClassName: string; }; -const ifmClassNames = { - note: 'secondary', - tip: 'success', - danger: 'danger', - info: 'info', - caution: 'warning', +const AdmonitionConfigs: {[key: string]: AdmonitionConfig | string} = { + note: { + infimaClassName: 'secondary', + iconComponent: NoteIcon, + }, + tip: { + infimaClassName: 'success', + iconComponent: TipIcon, + }, + danger: { + infimaClassName: 'danger', + iconComponent: DangerIcon, + }, + info: { + infimaClassName: 'info', + iconComponent: InfoIcon, + }, + caution: { + infimaClassName: 'warning', + iconComponent: CautionIcon, + }, + secondary: 'note', + important: 'info', + success: 'tip', + warning: 'danger', }; -export default function Admonition({ - children, - type, - title = type, - icon = icons[type], -}: Props): JSX.Element { +function getAdmonitionConfig(type: string): AdmonitionConfig { + const config = AdmonitionConfigs[type]; + if (config) { + if (typeof config === 'string') { + return AdmonitionConfigs[config] as AdmonitionConfig; + } + return config; + } + console.warn( + `No admonition config found for admonition type "${type}". Using Info as fallback.`, + ); + return AdmonitionConfigs.info as AdmonitionConfig; +} + +// Workaround because it's difficult in MDX v1 to provide a MDX title as props +// See https://github.com/facebook/docusaurus/pull/7152#issuecomment-1145779682 +function extractMDXAdmonitionTitle(children: ReactNode): { + mdxAdmonitionTitle: ReactNode | undefined; + rest: ReactNode; +} { + const items = React.Children.toArray(children); + const mdxAdmonitionTitle = items.find( + (item) => + React.isValidElement(item) && + (item.props as {mdxType: string} | null)?.mdxType === + 'mdxAdmonitionTitle', + ); + const rest = <>{items.filter((item) => item !== mdxAdmonitionTitle)}; + return { + mdxAdmonitionTitle, + rest, + }; +} + +function processAdmonitionProps(props: Props): Props { + const {mdxAdmonitionTitle, rest} = extractMDXAdmonitionTitle(props.children); + return { + ...props, + title: props.title ?? mdxAdmonitionTitle ?? props.type, + children: rest, + }; +} + +export default function Admonition(props: Props): JSX.Element { + const { + children, + type, + title = type, + icon: iconProp, + } = processAdmonitionProps(props); + + const config = getAdmonitionConfig(type); + const {infimaClassName, iconComponent: IconComponent} = config; + const icon = iconProp ?? ; return (
-
-
- {icon} - {title} -
+ className={clsx('alert', `alert--${infimaClassName}`, styles.admonition)}> +
+ {icon} + {title}
-
{children}
+
{children}
); } diff --git a/packages/docusaurus-theme-classic/src/admonitions.css b/packages/docusaurus-theme-classic/src/theme/Admonition/styles.module.css similarity index 59% rename from packages/docusaurus-theme-classic/src/admonitions.css rename to packages/docusaurus-theme-classic/src/theme/Admonition/styles.module.css index 2b5ac012d3e2..2580873f11e3 100644 --- a/packages/docusaurus-theme-classic/src/admonitions.css +++ b/packages/docusaurus-theme-classic/src/theme/Admonition/styles.module.css @@ -5,35 +5,34 @@ * LICENSE file in the root directory of this source tree. */ -.admonition h5 { - margin-top: 0; - margin-bottom: 8px; +.admonition { + margin-bottom: 1em; +} + +.admonitionHeading { + font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) / + var(--ifm-heading-line-height) var(--ifm-heading-font-family); text-transform: uppercase; + margin-bottom: 0.3rem; } -.admonition h5 code { +.admonitionHeading code { text-transform: none; } -.admonition-icon { +.admonitionIcon { display: inline-block; vertical-align: middle; margin-right: 0.4em; } -.admonition-icon svg { +.admonitionIcon svg { display: inline-block; - width: 22px; - height: 22px; - stroke-width: 0; + height: 1.6em; + width: 1.6em; fill: var(--ifm-alert-foreground-color); - stroke: var(--ifm-alert-foreground-color); } -.admonition-content > :last-child { +.admonitionContent > :last-child { margin-bottom: 0; } - -.admonition { - margin-bottom: 1em; -} diff --git a/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx b/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx index 82e7c5ac6b12..8b6141f5e2bc 100644 --- a/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx @@ -14,6 +14,7 @@ import MDXDetails from '@theme/MDXComponents/Details'; import MDXHeading from '@theme/MDXComponents/Heading'; import MDXUl from '@theme/MDXComponents/Ul'; import MDXImg from '@theme/MDXComponents/Img'; +import Admonition from '@theme/Admonition'; import type {MDXComponentsObject} from '@theme/MDXComponents'; @@ -31,6 +32,7 @@ const MDXComponents: MDXComponentsObject = { h4: (props) => , h5: (props) => , h6: (props) => , + admonition: Admonition, }; export default MDXComponents; diff --git a/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap b/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap index bfa30ac7873e..d49280b4147d 100644 --- a/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap +++ b/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap @@ -1,12 +1,32 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`validation schemas admonitionsSchema: for value=[] 1`] = `""value" must be of type object"`; +exports[`validation schemas admonitionsSchema: for value=[] 1`] = `""value" does not look like a valid admonitions config"`; -exports[`validation schemas admonitionsSchema: for value=3 1`] = `""value" must be of type object"`; +exports[`validation schemas admonitionsSchema: for value={"customTypes":{"myKeyword":{"keyword":"myKeyword","infima":true,"svg":""}}} 1`] = ` +"The Docusaurus admonitions system has changed, and the option "customTypes" does not exist anymore. +You now need to swizzle the admonitions component to provide UI customizations such as icons. +Please refer to https://github.com/facebook/docusaurus/pull/7152 for detailed upgrade instructions." +`; + +exports[`validation schemas admonitionsSchema: for value={"icons":"emoji"} 1`] = ` +"The Docusaurus admonitions system has changed, and the option "icons" does not exist anymore. +You now need to swizzle the admonitions component to provide UI customizations such as icons. +Please refer to https://github.com/facebook/docusaurus/pull/7152 for detailed upgrade instructions." +`; + +exports[`validation schemas admonitionsSchema: for value={"infima":true} 1`] = ` +"The Docusaurus admonitions system has changed, and the option "infima" does not exist anymore. +You now need to swizzle the admonitions component to provide UI customizations such as icons. +Please refer to https://github.com/facebook/docusaurus/pull/7152 for detailed upgrade instructions." +`; + +exports[`validation schemas admonitionsSchema: for value={"keywords":[]} 1`] = `""keywords" does not contain 1 required value(s)"`; + +exports[`validation schemas admonitionsSchema: for value={"tag":""} 1`] = `""tag" is not allowed to be empty"`; -exports[`validation schemas admonitionsSchema: for value=null 1`] = `""value" must be of type object"`; +exports[`validation schemas admonitionsSchema: for value={"unknownAttribute":"val"} 1`] = `""unknownAttribute" is not allowed"`; -exports[`validation schemas admonitionsSchema: for value=true 1`] = `""value" must be of type object"`; +exports[`validation schemas admonitionsSchema: for value=3 1`] = `""value" does not look like a valid admonitions config"`; exports[`validation schemas pathnameSchema: for value="foo" 1`] = `""value" is not a valid pathname. Pathname should start with slash and not contain any domain or query string."`; diff --git a/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts b/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts index 534ac218fd94..ad66dede975e 100644 --- a/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts +++ b/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts @@ -87,17 +87,39 @@ describe('validation schemas', () => { it('admonitionsSchema', () => { const {testOK, testFail} = createTestHelpers({ schema: AdmonitionsSchema, - defaultValue: {}, + defaultValue: true, }); testOK(undefined); + testOK(true); + testOK(false); testOK({}); - testOK({attr: 'val'}); + testOK({tag: '+++'}); + testOK({keywords: ['info', 'tip']}); + testOK({tag: '+++', keywords: ['info', 'tip']}); - testFail(null); testFail(3); - testFail(true); testFail([]); + testFail({unknownAttribute: 'val'}); + testFail({tag: ''}); + testFail({keywords: []}); + + // Legacy types + testFail({ + infima: true, + }); + testFail({ + icons: 'emoji', + }); + testFail({ + customTypes: { + myKeyword: { + keyword: `myKeyword`, + infima: true, + svg: '', + }, + }, + }); }); it('remarkPluginsSchema', () => { diff --git a/packages/docusaurus-utils-validation/src/validationSchemas.ts b/packages/docusaurus-utils-validation/src/validationSchemas.ts index 8c6a10e9c00b..f6bd54d6f3d8 100644 --- a/packages/docusaurus-utils-validation/src/validationSchemas.ts +++ b/packages/docusaurus-utils-validation/src/validationSchemas.ts @@ -32,7 +32,31 @@ const MarkdownPluginsSchema = Joi.array() export const RemarkPluginsSchema = MarkdownPluginsSchema; export const RehypePluginsSchema = MarkdownPluginsSchema; -export const AdmonitionsSchema = Joi.object().default({}); +const LegacyAdmonitionConfigSchema = Joi.forbidden().messages({ + 'any.unknown': `The Docusaurus admonitions system has changed, and the option {#label} does not exist anymore. +You now need to swizzle the admonitions component to provide UI customizations such as icons. +Please refer to https://github.com/facebook/docusaurus/pull/7152 for detailed upgrade instructions.`, +}); + +export const AdmonitionsSchema = JoiFrontMatter.alternatives() + .try( + JoiFrontMatter.boolean().required(), + JoiFrontMatter.object({ + tag: JoiFrontMatter.string(), + keywords: JoiFrontMatter.array().items( + JoiFrontMatter.string().required(), + ), + // TODO Remove before 2023 + customTypes: LegacyAdmonitionConfigSchema, + icons: LegacyAdmonitionConfigSchema, + infima: LegacyAdmonitionConfigSchema, + }).required(), + ) + .default(true) + .messages({ + 'alternatives.types': + '{{#label}} does not look like a valid admonitions config', + }); // TODO how can we make this emit a custom error message :'( // Joi is such a pain, good luck to annoying trying to improve this diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index fcc26af6e257..bbc5a0aac66a 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -90,7 +90,6 @@ "react-router": "^5.3.3", "react-router-config": "^5.1.1", "react-router-dom": "^5.3.3", - "remark-admonitions": "^1.2.1", "rtl-detect": "^1.0.4", "semver": "^7.3.7", "serve-handler": "^6.1.3", diff --git a/packages/docusaurus/src/deps.d.ts b/packages/docusaurus/src/deps.d.ts index c70f6b8b54b4..c781b107ab0f 100644 --- a/packages/docusaurus/src/deps.d.ts +++ b/packages/docusaurus/src/deps.d.ts @@ -5,8 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -declare module 'remark-admonitions'; - declare module 'react-loadable-ssr-addon-v5-slorber' { import type {WebpackPluginInstance, Compiler} from 'webpack'; diff --git a/packages/docusaurus/src/server/plugins/synthetic.ts b/packages/docusaurus/src/server/plugins/synthetic.ts index 5d4fe38b4d01..10425a29478d 100644 --- a/packages/docusaurus/src/server/plugins/synthetic.ts +++ b/packages/docusaurus/src/server/plugins/synthetic.ts @@ -6,7 +6,6 @@ */ import path from 'path'; -import admonitions from 'remark-admonitions'; import type {RuleSetRule} from 'webpack'; import type {HtmlTagObject, LoadedPlugin, LoadContext} from '@docusaurus/types'; @@ -97,6 +96,7 @@ export function createMDXFallbackPlugin({ }); } const mdxLoaderOptions = { + admonitions: true, staticDirs: siteConfig.staticDirectories.map((dir) => path.resolve(siteDir, dir), ), @@ -105,7 +105,6 @@ export function createMDXFallbackPlugin({ isMDXPartial: () => true, // External MDX files might have front matter, just disable the warning isMDXPartialFrontMatterWarningDisabled: true, - remarkPlugins: [admonitions], }; return { diff --git a/website/_dogfooding/_pages tests/markdownPageTests.md b/website/_dogfooding/_pages tests/markdownPageTests.md index dc8695b11179..604f60087ced 100644 --- a/website/_dogfooding/_pages tests/markdownPageTests.md +++ b/website/_dogfooding/_pages tests/markdownPageTests.md @@ -221,3 +221,11 @@ Can be arbitrarily nested: - [ ] Task - [ ] Task - [ ] Task + +## Admonitions + +:::caution Interpolated `title` with a + +Admonition body + +::: diff --git a/yarn.lock b/yarn.lock index 74fde4aecb87..8c84e6d48251 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5190,7 +5190,7 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001335: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz#8a1e7fdc4db9c2ec79a05e9fd68eb93a761888bb" integrity sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g== -ccount@^1.0.0, ccount@^1.0.3: +ccount@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== @@ -8120,17 +8120,6 @@ hast-to-hyperscript@^9.0.0: unist-util-is "^4.0.0" web-namespaces "^1.0.0" -hast-util-from-parse5@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c" - integrity sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA== - dependencies: - ccount "^1.0.3" - hastscript "^5.0.0" - property-information "^5.0.0" - web-namespaces "^1.1.2" - xtend "^4.0.1" - hast-util-from-parse5@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" @@ -8157,6 +8146,11 @@ hast-util-from-parse5@^7.0.0: vfile-location "^4.0.0" web-namespaces "^2.0.0" +hast-util-is-element@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425" + integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ== + hast-util-is-element@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz#fc0b0dc7cef3895e839b8d66979d57b0338c68f3" @@ -8193,6 +8187,22 @@ hast-util-raw@6.0.1: xtend "^4.0.0" zwitch "^1.0.0" +hast-util-to-html@^7.1.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz#9f339ca9bea71246e565fc79ff7dbfe98bb50f5e" + integrity sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw== + dependencies: + ccount "^1.0.0" + comma-separated-tokens "^1.0.0" + hast-util-is-element "^1.0.0" + hast-util-whitespace "^1.0.0" + html-void-elements "^1.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + stringify-entities "^3.0.1" + unist-util-is "^4.0.0" + xtend "^4.0.0" + hast-util-to-parse5@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" @@ -8218,15 +8228,10 @@ hast-util-to-text@^3.1.0: hast-util-is-element "^2.0.0" unist-util-find-after "^4.0.0" -hastscript@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.1.2.tgz#bde2c2e56d04c62dd24e8c5df288d050a355fb8a" - integrity sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ== - dependencies: - comma-separated-tokens "^1.0.0" - hast-util-parse-selector "^2.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" +hast-util-whitespace@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz#e4fe77c4a9ae1cb2e6c25e02df0043d0164f6e41" + integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A== hastscript@^6.0.0: version "6.0.0" @@ -10365,6 +10370,20 @@ mdast-util-to-hast@10.0.1: unist-util-position "^3.0.0" unist-util-visit "^2.0.0" +mdast-util-to-hast@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz#61875526a017d8857b71abc9333942700b2d3604" + integrity sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + mdast-util-to-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" @@ -11562,7 +11581,7 @@ parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parse5@^5.0.0, parse5@^5.1.1: +parse5@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== @@ -12920,15 +12939,6 @@ rehype-katex@^6.0.2: unist-util-remove-position "^4.0.0" unist-util-visit "^4.0.0" -rehype-parse@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.2.tgz#aeb3fdd68085f9f796f1d3137ae2b85a98406964" - integrity sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug== - dependencies: - hast-util-from-parse5 "^5.0.0" - parse5 "^5.0.0" - xtend "^4.0.0" - rehype-parse@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-7.0.1.tgz#58900f6702b56767814afc2a9efa2d42b1c90c57" @@ -12947,20 +12957,18 @@ rehype-parse@^8.0.0: parse5 "^6.0.0" unified "^10.0.0" +rehype-stringify@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-8.0.0.tgz#9b6afb599bcf3165f10f93fc8548f9a03d2ec2ba" + integrity sha512-VkIs18G0pj2xklyllrPSvdShAV36Ff3yE5PUO9u36f6+2qJFnn22Z5gKwBOwgXviux4UC7K+/j13AnZfPICi/g== + dependencies: + hast-util-to-html "^7.1.1" + relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== -remark-admonitions@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/remark-admonitions/-/remark-admonitions-1.2.1.tgz#87caa1a442aa7b4c0cafa04798ed58a342307870" - integrity sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow== - dependencies: - rehype-parse "^6.0.2" - unified "^8.4.2" - unist-util-visit "^2.0.1" - remark-emoji@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" @@ -13016,6 +13024,13 @@ remark-parse@8.0.3, remark-parse@^8.0.0, remark-parse@^8.0.2: vfile-location "^3.0.0" xtend "^4.0.1" +remark-rehype@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-8.1.0.tgz#610509a043484c1e697437fa5eb3fd992617c945" + integrity sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA== + dependencies: + mdast-util-to-hast "^10.2.0" + remark-squeeze-paragraphs@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" @@ -14883,17 +14898,6 @@ unified@^10.0.0: trough "^2.0.0" vfile "^5.0.0" -unified@^8.4.2: - version "8.4.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-8.4.2.tgz#13ad58b4a437faa2751a4a4c6a16f680c500fff1" - integrity sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA== - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" - unified@^9.0.0, unified@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" @@ -15029,7 +15033,7 @@ unist-util-visit-parents@^5.0.0: "@types/unist" "^2.0.0" unist-util-is "^5.0.0" -unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.1, unist-util-visit@^2.0.3: +unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== @@ -15372,7 +15376,7 @@ wcwidth@^1.0.0: dependencies: defaults "^1.0.3" -web-namespaces@^1.0.0, web-namespaces@^1.1.2: +web-namespaces@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==