diff --git a/jest/deps.d.ts b/jest/deps.d.ts index 4f7717ddd594..93d80cc3d665 100644 --- a/jest/deps.d.ts +++ b/jest/deps.d.ts @@ -13,10 +13,6 @@ declare module 'to-vfile' { export function read(path: string, encoding?: string): Promise; } -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 70e8f18ea8be..ea035b4fb546 100644 --- a/packages/docusaurus-mdx-loader/package.json +++ b/packages/docusaurus-mdx-loader/package.json @@ -32,6 +32,7 @@ "image-size": "^1.0.2", "mdast-util-to-string": "^3.0.0", "mdast-util-mdx": "^2.0.0", + "rehype-raw": "^6.1.1", "remark-comment": "^1.0.0", "remark-gfm": "^3.0.1", "remark-directive": "^2.0.1", diff --git a/packages/docusaurus-mdx-loader/src/__tests__/format.test.ts b/packages/docusaurus-mdx-loader/src/__tests__/format.test.ts new file mode 100644 index 000000000000..955181401693 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/__tests__/format.test.ts @@ -0,0 +1,52 @@ +/** + * 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 {getFormat} from '../format'; + +describe('getFormat', () => { + it('uses frontMatter format over anything else', () => { + expect(getFormat({frontMatterFormat: 'md', filePath: 'xyz.md'})).toBe('md'); + expect(getFormat({frontMatterFormat: 'md', filePath: 'xyz.mdx'})).toBe( + 'md', + ); + expect(getFormat({frontMatterFormat: 'mdx', filePath: 'xyz.md'})).toBe( + 'mdx', + ); + expect(getFormat({frontMatterFormat: 'mdx', filePath: 'xyz.mdx'})).toBe( + 'mdx', + ); + }); + + it('detects appropriate format from file extension', () => { + expect(getFormat({frontMatterFormat: 'detect', filePath: 'xyz.md'})).toBe( + 'md', + ); + expect( + getFormat({frontMatterFormat: 'detect', filePath: 'xyz.markdown'}), + ).toBe('md'); + + expect( + getFormat({frontMatterFormat: 'detect', filePath: 'folder/xyz.md'}), + ).toBe('md'); + expect( + getFormat({frontMatterFormat: 'detect', filePath: 'folder/xyz.markdown'}), + ).toBe('md'); + expect(getFormat({frontMatterFormat: 'detect', filePath: 'xyz.mdx'})).toBe( + 'mdx', + ); + expect( + getFormat({frontMatterFormat: 'detect', filePath: 'folder/xyz.mdx'}), + ).toBe('mdx'); + + expect( + getFormat({frontMatterFormat: 'detect', filePath: 'xyz.unknown'}), + ).toBe('mdx'); + expect( + getFormat({frontMatterFormat: 'detect', filePath: 'folder/xyz.unknown'}), + ).toBe('mdx'); + }); +}); diff --git a/packages/docusaurus-mdx-loader/src/__tests__/processor.test.ts b/packages/docusaurus-mdx-loader/src/__tests__/processor.test.ts new file mode 100644 index 000000000000..2dee34638645 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/__tests__/processor.test.ts @@ -0,0 +1,33 @@ +/** + * 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 {createProcessor} from '../processor'; +// import type {Options} from '../loader'; + +/* +async function testProcess({ + format, + options, +}: { + format: 'md' | 'mdx'; + options: Options; +}) { + return async (content: string) => { + const processor = await createProcessor({format, options}); + return processor.process(content); + }; +} + */ + +describe('md processor', () => { + it('parses simple commonmark', async () => { + // TODO no tests for now, wait until ESM support + // Jest does not support well ESM modules + // It would require to vendor too much Unified modules as CJS + // See https://mdxjs.com/docs/troubleshooting-mdx/#esm + }); +}); diff --git a/packages/docusaurus-mdx-loader/src/format.ts b/packages/docusaurus-mdx-loader/src/format.ts new file mode 100644 index 000000000000..9b282e945a43 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/format.ts @@ -0,0 +1,40 @@ +/** + * 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 type {MDXFrontMatter} from './frontMatter'; + +// Copied from https://mdxjs.com/packages/mdx/#optionsmdextensions +// Although we are likely to only use .md / .mdx anyway... +const mdFormatExtensions = [ + '.md', + '.markdown', + '.mdown', + '.mkdn', + '.mkd', + '.mdwn', + '.mkdown', + '.ron', +]; + +function isMDFormat(filepath: string) { + return mdFormatExtensions.includes(path.extname(filepath)); +} + +export function getFormat({ + filePath, + frontMatterFormat, +}: { + filePath: string; + frontMatterFormat: MDXFrontMatter['format']; +}): 'md' | 'mdx' { + if (frontMatterFormat !== 'detect') { + return frontMatterFormat; + } + // Bias toward mdx if unknown extension + return isMDFormat(filePath) ? 'md' : 'mdx'; +} diff --git a/packages/docusaurus-mdx-loader/src/index.ts b/packages/docusaurus-mdx-loader/src/index.ts index f242ebdcf3a0..19e1a2236057 100644 --- a/packages/docusaurus-mdx-loader/src/index.ts +++ b/packages/docusaurus-mdx-loader/src/index.ts @@ -30,4 +30,6 @@ export type LoadedMDXContent = { readonly assets: Assets; (): JSX.Element; }; -export type {Options, MDXPlugin, MDXOptions} from './loader'; + +export type {Options, MDXPlugin} from './loader'; +export type {MDXOptions} from './processor'; diff --git a/packages/docusaurus-mdx-loader/src/loader.ts b/packages/docusaurus-mdx-loader/src/loader.ts index a93bb955bde4..d62b73b7d27f 100644 --- a/packages/docusaurus-mdx-loader/src/loader.ts +++ b/packages/docusaurus-mdx-loader/src/loader.ts @@ -6,7 +6,6 @@ */ import fs from 'fs-extra'; -import path from 'path'; import logger from '@docusaurus/logger'; import { parseFrontMatter, @@ -14,82 +13,26 @@ import { escapePath, getFileLoaderUtils, } from '@docusaurus/utils'; -import emoji from 'remark-emoji'; import stringifyObject from 'stringify-object'; import preprocessor from './preprocessor'; -import headings from './remark/headings'; -import toc from './remark/toc'; -import transformImage from './remark/transformImage'; -import transformLinks from './remark/transformLinks'; -import details from './remark/details'; -import head from './remark/head'; -import mermaid from './remark/mermaid'; -import transformAdmonitions from './remark/admonitions'; -import codeCompatPlugin from './remark/mdx1Compat/codeCompatPlugin'; import {validateMDXFrontMatter} from './frontMatter'; +import {createProcessorCached} from './processor'; +import type {MDXOptions} from './processor'; import type {MarkdownConfig} from '@docusaurus/types'; import type {LoaderContext} from 'webpack'; -// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 -import type {Processor} from 'unified'; -import type {AdmonitionOptions} from './remark/admonitions'; - -// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 -import type {ProcessorOptions} from '@mdx-js/mdx'; - // TODO as of April 2023, no way to import/re-export this ESM type easily :/ // This might change soon, likely after TS 5.2 // See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391 type Pluggable = any; // TODO fix this asap -// Copied from https://mdxjs.com/packages/mdx/#optionsmdextensions -// Although we are likely to only use .md / .mdx anyway... -const mdFormatExtensions = [ - '.md', - '.markdown', - '.mdown', - '.mkdn', - '.mkd', - '.mdwn', - '.mkdown', - '.ron', -]; - -function isMDFormat(filepath: string) { - return mdFormatExtensions.includes(path.extname(filepath)); -} - const { loaders: {inlineMarkdownImageFileLoader}, } = getFileLoaderUtils(); -const DEFAULT_OPTIONS: MDXOptions = { - admonitions: true, - rehypePlugins: [], - remarkPlugins: [emoji, headings, toc], - beforeDefaultRemarkPlugins: [], - beforeDefaultRehypePlugins: [], -}; - -type CompilerCacheEntry = { - mdCompiler: Processor; - mdxCompiler: Processor; - options: Options; -}; - -const compilerCache = new Map(); - export type MDXPlugin = Pluggable; -export type MDXOptions = { - admonitions: boolean | Partial; - remarkPlugins: MDXPlugin[]; - rehypePlugins: MDXPlugin[]; - beforeDefaultRemarkPlugins: MDXPlugin[]; - beforeDefaultRehypePlugins: MDXPlugin[]; -}; - export type Options = Partial & { markdownConfig: MarkdownConfig; staticDirs: string[]; @@ -167,20 +110,6 @@ 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 []; -} - // TODO temporary, remove this after v3.1? // Some plugin authors use our mdx-loader, despite it not being public API // see https://github.com/facebook/docusaurus/issues/8298 @@ -198,14 +127,10 @@ export async function mdxLoader( ): Promise { const callback = this.async(); const filePath = this.resourcePath; - const reqOptions = this.getOptions(); + const reqOptions: Options = this.getOptions(); + const {query} = this; ensureMarkdownConfig(reqOptions); - const {createProcessor} = await import('@mdx-js/mdx'); - const {default: gfm} = await import('remark-gfm'); - const {default: comment} = await import('remark-comment'); - const {default: directives} = await import('remark-directive'); - const {frontMatter, content: contentWithTitle} = parseFrontMatter(fileString); const mdxFrontMatter = validateMDXFrontMatter(frontMatter.mdx); @@ -225,93 +150,16 @@ export async function mdxLoader( const hasFrontMatter = Object.keys(frontMatter).length > 0; - if (!compilerCache.has(this.query)) { - /* - /!\ DO NOT PUT ANY ASYNC / AWAIT / DYNAMIC IMPORTS HERE - This creates cache creation race conditions - TODO extract this in a synchronous method - */ - - const remarkPlugins: MDXPlugin[] = [ - ...(reqOptions.beforeDefaultRemarkPlugins ?? []), - directives, - ...getAdmonitionsPlugins(reqOptions.admonitions ?? false), - ...DEFAULT_OPTIONS.remarkPlugins, - details, - head, - ...(reqOptions.markdownConfig.mermaid ? [mermaid] : []), - [ - transformImage, - { - staticDirs: reqOptions.staticDirs, - siteDir: reqOptions.siteDir, - }, - ], - [ - transformLinks, - { - staticDirs: reqOptions.staticDirs, - siteDir: reqOptions.siteDir, - }, - ], - gfm, - reqOptions.markdownConfig.mdx1Compat.comments ? comment : null, - ...(reqOptions.remarkPlugins ?? []), - ].filter((plugin): plugin is MDXPlugin => Boolean(plugin)); - - // codeCompatPlugin needs to be applied last after user-provided plugins - // (after npm2yarn for example) - remarkPlugins.push(codeCompatPlugin); - - const rehypePlugins: MDXPlugin[] = [ - ...(reqOptions.beforeDefaultRehypePlugins ?? []), - ...DEFAULT_OPTIONS.rehypePlugins, - ...(reqOptions.rehypePlugins ?? []), - ]; - - const options: ProcessorOptions & Options = { - ...reqOptions, - remarkPlugins, - rehypePlugins, - providerImportSource: '@mdx-js/react', - }; - - const compilerCacheEntry: CompilerCacheEntry = { - mdCompiler: createProcessor({ - ...options, - format: 'md', - }), - mdxCompiler: createProcessor({ - ...options, - format: 'mdx', - }), - options, - }; - - compilerCache.set(this.query, compilerCacheEntry); - } - - const {mdCompiler, mdxCompiler, options} = compilerCache.get(this.query)!; - - function getCompiler() { - const format = - mdxFrontMatter.format === 'detect' - ? isMDFormat(filePath) - ? 'md' - : 'mdx' - : mdxFrontMatter.format; - - return format === 'md' ? mdCompiler : mdxCompiler; - } + const processor = await createProcessorCached({ + filePath, + reqOptions, + query, + mdxFrontMatter, + }); let result: string; try { - result = await getCompiler() - .process({ - value: content, - path: filePath, - }) - .then((res) => res.toString()); + result = await processor.process({content, filePath}); } catch (errorUnknown) { const error = errorUnknown as Error; return callback( @@ -327,14 +175,14 @@ export async function mdxLoader( // MDX partials are MDX files starting with _ or in a folder starting with _ // Partial are not expected to have associated metadata files or front matter - const isMDXPartial = options.isMDXPartial?.(filePath); + const isMDXPartial = reqOptions.isMDXPartial?.(filePath); if (isMDXPartial && hasFrontMatter) { const errorMessage = `Docusaurus MDX partial files should not contain front matter. Those partial files use the _ prefix as a convention by default, but this is configurable. File at ${filePath} contains front matter that will be ignored: ${JSON.stringify(frontMatter, null, 2)}`; - if (!options.isMDXPartialFrontMatterWarningDisabled) { + if (!reqOptions.isMDXPartialFrontMatterWarningDisabled) { const shouldError = process.env.NODE_ENV === 'test' || process.env.CI; if (shouldError) { return callback(new Error(errorMessage)); @@ -346,8 +194,11 @@ ${JSON.stringify(frontMatter, null, 2)}`; function getMetadataPath(): string | undefined { if (!isMDXPartial) { // Read metadata for this MDX and export it. - if (options.metadataPath && typeof options.metadataPath === 'function') { - return options.metadataPath(filePath); + if ( + reqOptions.metadataPath && + typeof reqOptions.metadataPath === 'function' + ) { + return reqOptions.metadataPath(filePath); } } return undefined; diff --git a/packages/docusaurus-mdx-loader/src/processor.ts b/packages/docusaurus-mdx-loader/src/processor.ts new file mode 100644 index 000000000000..95279e428ec4 --- /dev/null +++ b/packages/docusaurus-mdx-loader/src/processor.ts @@ -0,0 +1,241 @@ +/** + * 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 emoji from 'remark-emoji'; +import headings from './remark/headings'; +import toc from './remark/toc'; +import transformImage from './remark/transformImage'; +import transformLinks from './remark/transformLinks'; +import details from './remark/details'; +import head from './remark/head'; +import mermaid from './remark/mermaid'; +import transformAdmonitions from './remark/admonitions'; +import codeCompatPlugin from './remark/mdx1Compat/codeCompatPlugin'; +import {getFormat} from './format'; +import type {MDXFrontMatter} from './frontMatter'; +import type {Options} from './loader'; +import type {AdmonitionOptions} from './remark/admonitions'; + +// @ts-expect-error: TODO see https://github.com/microsoft/TypeScript/issues/49721 +import type {ProcessorOptions} from '@mdx-js/mdx'; + +// TODO as of April 2023, no way to import/re-export this ESM type easily :/ +// This might change soon, likely after TS 5.2 +// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391 +type Pluggable = any; // TODO fix this asap + +// TODO alt interface because impossible to import type Processor (ESM + TS :/) +type SimpleProcessor = { + process: ({ + content, + filePath, + }: { + content: string; + filePath: string; + }) => Promise; +}; + +const DEFAULT_OPTIONS: MDXOptions = { + admonitions: true, + rehypePlugins: [], + remarkPlugins: [emoji, headings, toc], + beforeDefaultRemarkPlugins: [], + beforeDefaultRehypePlugins: [], +}; + +export type MDXPlugin = Pluggable; + +export type MDXOptions = { + admonitions: boolean | Partial; + remarkPlugins: MDXPlugin[]; + rehypePlugins: MDXPlugin[]; + beforeDefaultRemarkPlugins: MDXPlugin[]; + beforeDefaultRehypePlugins: MDXPlugin[]; +}; + +function getAdmonitionsPlugins( + admonitionsOption: MDXOptions['admonitions'], +): MDXPlugin[] { + if (admonitionsOption) { + const plugin: MDXPlugin = + admonitionsOption === true + ? transformAdmonitions + : [transformAdmonitions, admonitionsOption]; + return [plugin]; + } + + return []; +} + +// Need to be async due to ESM dynamic imports... +async function createProcessorFactory() { + const {createProcessor: createMdxProcessor} = await import('@mdx-js/mdx'); + const {default: rehypeRaw} = await import('rehype-raw'); + const {default: gfm} = await import('remark-gfm'); + const {default: comment} = await import('remark-comment'); + const {default: directive} = await import('remark-directive'); + + // /!\ this method is synchronous on purpose + // Using async code here can create cache entry race conditions! + function createProcessorSync({ + options, + format, + }: { + options: Options; + format: 'md' | 'mdx'; + }): SimpleProcessor { + const remarkPlugins: MDXPlugin[] = [ + ...(options.beforeDefaultRemarkPlugins ?? []), + directive, + ...getAdmonitionsPlugins(options.admonitions ?? false), + ...DEFAULT_OPTIONS.remarkPlugins, + details, + head, + ...(options.markdownConfig.mermaid ? [mermaid] : []), + [ + transformImage, + { + staticDirs: options.staticDirs, + siteDir: options.siteDir, + }, + ], + [ + transformLinks, + { + staticDirs: options.staticDirs, + siteDir: options.siteDir, + }, + ], + gfm, + options.markdownConfig.mdx1Compat.comments ? comment : null, + ...(options.remarkPlugins ?? []), + ].filter((plugin): plugin is MDXPlugin => Boolean(plugin)); + + // codeCompatPlugin needs to be applied last after user-provided plugins + // (after npm2yarn for example) + remarkPlugins.push(codeCompatPlugin); + + const rehypePlugins: MDXPlugin[] = [ + ...(options.beforeDefaultRehypePlugins ?? []), + ...DEFAULT_OPTIONS.rehypePlugins, + ...(options.rehypePlugins ?? []), + ]; + + if (format === 'md') { + // This is what permits to embed HTML elements with format 'md' + // See https://github.com/facebook/docusaurus/pull/8960 + // See https://github.com/mdx-js/mdx/pull/2295#issuecomment-1540085960 + const rehypeRawPlugin: MDXPlugin = [ + rehypeRaw, + { + passThrough: [ + 'mdxFlowExpression', + 'mdxJsxFlowElement', + 'mdxJsxTextElement', + 'mdxTextExpression', + 'mdxjsEsm', + ], + }, + ]; + rehypePlugins.unshift(rehypeRawPlugin); + } + + const processorOptions: ProcessorOptions & Options = { + ...options, + remarkPlugins, + rehypePlugins, + providerImportSource: '@mdx-js/react', + }; + + const mdxProcessor = createMdxProcessor({ + ...processorOptions, + format, + }); + + return { + process: async ({content, filePath}) => + mdxProcessor + .process({ + value: content, + path: filePath, + }) + .then((res) => res.toString()), + }; + } + + return {createProcessorSync}; +} + +// Will be useful for tests +export async function createProcessorUncached(parameters: { + options: Options; + format: 'md' | 'mdx'; +}): Promise { + const {createProcessorSync} = await createProcessorFactory(); + return createProcessorSync(parameters); +} + +// We use different compilers depending on the file type (md vs mdx) +type ProcessorsCacheEntry = { + mdProcessor: SimpleProcessor; + mdxProcessor: SimpleProcessor; +}; + +// Compilers are cached so that Remark/Rehype plugins can run +// expensive code during initialization +const ProcessorsCache = new Map(); + +async function createProcessorsCacheEntry({ + query, + reqOptions, +}: { + query: string | Options; + reqOptions: Options; +}): Promise { + const {createProcessorSync} = await createProcessorFactory(); + + const compilers = ProcessorsCache.get(query); + if (compilers) { + return compilers; + } + + const compilerCacheEntry: ProcessorsCacheEntry = { + mdProcessor: createProcessorSync({ + options: reqOptions, + format: 'md', + }), + mdxProcessor: createProcessorSync({ + options: reqOptions, + format: 'mdx', + }), + }; + + ProcessorsCache.set(query, compilerCacheEntry); + + return compilerCacheEntry; +} + +export async function createProcessorCached({ + filePath, + mdxFrontMatter, + query, + reqOptions, +}: { + filePath: string; + mdxFrontMatter: MDXFrontMatter; + query: string | Options; + reqOptions: Options; +}): Promise { + const compilers = await createProcessorsCacheEntry({query, reqOptions}); + + const format = getFormat({ + filePath, + frontMatterFormat: mdxFrontMatter.format, + }); + + return format === 'md' ? compilers.mdProcessor : compilers.mdxProcessor; +} diff --git a/website/_dogfooding/_pages tests/markdown-tests-md.md b/website/_dogfooding/_pages tests/markdown-tests-md.md index a0d9800eb677..7b3919884011 100644 --- a/website/_dogfooding/_pages tests/markdown-tests-md.md +++ b/website/_dogfooding/_pages tests/markdown-tests-md.md @@ -23,7 +23,7 @@ import BrowserWindow from '@site/src/components/BrowserWindow'; BrowserWindow content - + export const answer = 42; @@ -42,3 +42,55 @@ note ## Heading Id {#custom-heading-id} Custom heading syntax `{#custom-heading-id}` still works + +--- + +## HTML + +### Styling + +blue span + +

green p

+ + + +
+ lime red +
+ +
+ +### Embeds + +#### Closed image tag: + + + +
+ +#### Unclosed image tag: + + + +
+ +### Iframe + + + +
+ +### Security + +```md +

+ When pressing this button, no alert should be printed + +

+``` + +

+ When pressing this button, no alert should be printed + +

diff --git a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx index 5f5c4b635fb6..bdc1449456a1 100644 --- a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx +++ b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx @@ -4,6 +4,8 @@ description: Markdown Page tests description wrapperClassName: docusaurus-markdown-example --- + + # Markdown .mdx tests This is a page generated from Markdown to illustrate the Markdown page feature and test some edge cases. diff --git a/yarn.lock b/yarn.lock index ab9c1c53ce22..6b069a65fa40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3268,6 +3268,11 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== +"@types/parse5@^6.0.0": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" + integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== + "@types/picomatch@^2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" @@ -8235,6 +8240,23 @@ hast-util-parse-selector@^3.0.0: dependencies: "@types/hast" "^2.0.0" +hast-util-raw@^7.2.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-7.2.3.tgz#dcb5b22a22073436dbdc4aa09660a644f4991d99" + integrity sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg== + dependencies: + "@types/hast" "^2.0.0" + "@types/parse5" "^6.0.0" + hast-util-from-parse5 "^7.0.0" + hast-util-to-parse5 "^7.0.0" + html-void-elements "^2.0.0" + parse5 "^6.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + vfile "^5.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + hast-util-to-estree@^2.0.0: version "2.3.2" resolved "https://registry.yarnpkg.com/hast-util-to-estree/-/hast-util-to-estree-2.3.2.tgz#11ab0cd2e70ecf0305151af56e636b1cdfbba0bf" @@ -8272,6 +8294,18 @@ hast-util-to-html@^7.1.1: unist-util-is "^4.0.0" xtend "^4.0.0" +hast-util-to-parse5@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz#c49391bf8f151973e0c9adcd116b561e8daf29f3" + integrity sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + hast-util-to-string@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-1.0.4.tgz#9b24c114866bdb9478927d7e9c36a485ac728378" @@ -8442,6 +8476,11 @@ html-void-elements@^1.0.0: resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== +html-void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f" + integrity sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A== + html-webpack-plugin@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" @@ -13833,6 +13872,15 @@ rehype-parse@^8.0.0: parse5 "^6.0.0" unified "^10.0.0" +rehype-raw@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/rehype-raw/-/rehype-raw-6.1.1.tgz#81bbef3793bd7abacc6bf8335879d1b6c868c9d4" + integrity sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ== + dependencies: + "@types/hast" "^2.0.0" + hast-util-raw "^7.2.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"