diff --git a/docs/_asset/index.css b/docs/_asset/index.css index c2a9abec4..1074d63b1 100644 --- a/docs/_asset/index.css +++ b/docs/_asset/index.css @@ -912,6 +912,16 @@ button.success { background-color: var(--fg); } +details { + border: 1px solid var(--gray-2); + padding: 1ex; + border-radius: 3px; +} + +details[open] { + padding: calc(1em + 1ex); +} + @media (prefers-color-scheme: dark) { :root { --white: #f0f6fc; @@ -1095,6 +1105,10 @@ button.success { border-color: var(--green-5); color: var(--white); } + + details { + border-color: var(--gray-6); + } } @media (min-width: 22em) { diff --git a/docs/docs/extending-mdx.mdx b/docs/docs/extending-mdx.mdx index 95a21a11b..5fd072516 100644 --- a/docs/docs/extending-mdx.mdx +++ b/docs/docs/extending-mdx.mdx @@ -104,8 +104,8 @@ You can use this template: ## Using plugins Where to pass plugins is encoded in their name: remark plugins go in -[`options.remarkPlugins`][options-remark-plugins], rehype plugins -in [`options.rehypePlugins`][options-rehype-plugins]. +`remarkPlugins` and rehype plugins go in `rehypePlugins` of +[`ProcessorOptions`][processor-options]. Those fields expect lists of plugins and/or of `[plugin, options]`: ```tsx @@ -169,9 +169,7 @@ For info on the node types that represent MDX specific features, see [start]: /docs/getting-started/ -[options-remark-plugins]: /packages/mdx/#optionsremarkplugins - -[options-rehype-plugins]: /packages/mdx/#optionsrehypeplugins +[processor-options]: /packages/mdx/#processoroptions [architecture]: /packages/mdx/#architecture diff --git a/docs/docs/getting-started.mdx b/docs/docs/getting-started.mdx index eb8e2d30d..17b1328a8 100644 --- a/docs/docs/getting-started.mdx +++ b/docs/docs/getting-started.mdx @@ -108,28 +108,31 @@ your JSX runtime. you don’t need to do anything. Optionally install and configure [`@mdx-js/react`][mdx-react] * If you’re using **Preact**, - set [`options.jsxImportSource`][options-jsximportsource] to `'preact'`. + set [`jsxImportSource` in `ProcessorOptions`][processor-options] to + `'preact'`. Optionally install and configure [`@mdx-js/preact`][mdx-preact] * If you’re using **Svelte**, install [`svelte-jsx`][svelte-jsx]. - Set [`options.jsxImportSource`][options-jsximportsource] to `'svelte-jsx'`. + Set [`jsxImportSource` in `ProcessorOptions`][processor-options] to + `'svelte-jsx'`. * If you’re using **Vue 3**, - set [`options.jsx`][options-jsx] to `true`. + set [`jsx` in `ProcessorOptions`][processor-options] to `true`. Then use Babel alongside your MDX integration (which is possible with webpack and Rollup but not esbuild) and configure it to use [`@vue/babel-plugin-jsx`](https://github.com/vuejs/jsx-next/tree/dev/packages/babel-plugin-jsx). Optionally install and configure [`@mdx-js/vue`][mdx-vue] * If you’re using **Emotion**, - set [`options.jsxImportSource`][options-jsximportsource] to + set [`jsxImportSource` in `ProcessorOptions`][processor-options] to `'@emotion/react'` * If you’re using **Theme UI**, install and configure [`@mdx-js/react`][mdx-react]. Then wrap your MDX content in a `` * If you’re using **Solid**, - set [`options.jsxImportSource`][options-jsximportsource] to `'solid-js/h'` + set [`jsxImportSource` in `ProcessorOptions`][processor-options] to + `'solid-js/h'` Other JSX runtimes are supported by setting -[`options.jsxImportSource`][options-jsximportsource]. +[`jsxImportSource` in `ProcessorOptions`][processor-options]. See also the different options there on how to use the classic JSX runtime and how to define a `pragma` and `pragmaFrag` for it. @@ -719,7 +722,7 @@ on how to use MDX with React Static. [Emotion](https://emotion.sh/docs/introduction) is supported when -[`options.jsxImportSource`][options-jsximportsource] is set to +[`jsxImportSource` in `ProcessorOptions`][processor-options] is set to `'@emotion/react'`. You can optionally install and configure [`@mdx-js/react`][mdx-react], which allows for context based component passing. @@ -780,8 +783,8 @@ more info. ``` -Preact is supported when [`options.jsxImportSource`][options-jsximportsource] is -set to `'preact'`. +Preact is supported when [`jsxImportSource` in `ProcessorOptions`][processor-options] +is set to `'preact'`. You can optionally install and configure [`@mdx-js/preact`][mdx-preact], which allows for context based component passing. @@ -856,8 +859,8 @@ info. ``` -Svelte is supported when [`options.jsxImportSource`][options-jsximportsource] is -set to `'svelte-jsx'`, which is a [small package][svelte-jsx] that adds support +Svelte is supported when [`jsxImportSource` in `ProcessorOptions`][processor-options] +is set to `'svelte-jsx'`, which is a [small package][svelte-jsx] that adds support for the JSX automatic runtime to Svelte. See also [¶ esbuild][esbuild], [¶ Rollup][rollup], and [¶ webpack][webpack], @@ -899,8 +902,8 @@ for more info. ``` -Solid is supported when [`options.jsxImportSource`][options-jsximportsource] is -set to `'solid-js/h'`. +Solid is supported when [`jsxImportSource` in `ProcessorOptions`][processor-options] +is set to `'solid-js/h'`. See also [¶ Vite][vite] and [¶ Rollup][rollup] which you might be using, for more info. @@ -961,9 +964,7 @@ See their readmes on how to configure them. [evaluate]: /packages/mdx/#evaluatefile-options -[options-jsximportsource]: /packages/mdx/#optionsjsximportsource - -[options-jsx]: /packages/mdx/#optionsjsx +[processor-options]: /packages/mdx/#processoroptions [cra]: #create-react-app-cra diff --git a/docs/docs/using-mdx.mdx b/docs/docs/using-mdx.mdx index 490cc73b5..4bde13b93 100644 --- a/docs/docs/using-mdx.mdx +++ b/docs/docs/using-mdx.mdx @@ -377,7 +377,7 @@ Set it up like so: or [`@mdx-js/vue`][mdx-vue], depending on what framework you’re using 2. Configure your MDX integration with - [`options.providerImportSource`][options-provider-import-source] + [`providerImportSource` in `ProcessorOptions`][processor-options] set to that package, so either `'@mdx-js/react'`, `'@mdx-js/preact'`, or `'@mdx-js/vue'` 3. Import `MDXProvider` from that package. @@ -478,7 +478,7 @@ providers: pass components explicitly. [mdx-vue]: /packages/vue/ -[options-provider-import-source]: /packages/mdx/#optionsproviderimportsource +[processor-options]: /packages/mdx/#processoroptions [emotion]: /docs/getting-started/#emotion diff --git a/docs/docs/what-is-mdx.mdx b/docs/docs/what-is-mdx.mdx index b29d6d41d..111487742 100644 --- a/docs/docs/what-is-mdx.mdx +++ b/docs/docs/what-is-mdx.mdx @@ -78,7 +78,7 @@ in braces (`{1 + 1}`) and ESM (`import` and `export`). Or use it always. If you’re using a bundler integration you can change between MDX and markdown through the file extension (`.mdx` vs. `.md`). - Alternatively, [`options.format`][format] can be used. + Alternatively, `options.format` can be used. The MDX syntax combines markdown with JSX. @@ -347,8 +347,6 @@ the heading. [jsx-spec]: https://facebook.github.io/jsx/ -[format]: /packages/mdx/#optionsformat - [start]: /docs/getting-started/ [trouble]: /docs/troubleshooting-mdx/ diff --git a/docs/guides/frontmatter.mdx b/docs/guides/frontmatter.mdx index a89444591..178661a1f 100644 --- a/docs/guides/frontmatter.mdx +++ b/docs/guides/frontmatter.mdx @@ -87,8 +87,8 @@ title: Hi, World! That’s exactly what the remark plugin [`remark-mdx-frontmatter`][remark-mdx-frontmatter] does. -remark plugins can be passed in -[`options.remarkPlugins`][options-remark-plugins]. +remark plugins can be passed as +[`remarkPlugins` in `ProcessorOptions`][processor-options]. More info on plugins is available in [§ Extending MDX][extend] [commonmark]: https://spec.commonmark.org/current/ @@ -97,6 +97,6 @@ More info on plugins is available in [§ Extending MDX][extend] [remark-mdx-frontmatter]: https://github.com/remcohaszing/remark-mdx-frontmatter -[options-remark-plugins]: /packages/mdx/#optionsremarkplugins +[processor-options]: /packages/mdx/#processoroptions [extend]: /docs/extending-mdx/ diff --git a/docs/guides/gfm.mdx b/docs/guides/gfm.mdx index b8fb7d9e7..40d6e4bdc 100644 --- a/docs/guides/gfm.mdx +++ b/docs/guides/gfm.mdx @@ -15,7 +15,8 @@ MDX supports standard markdown syntax ([CommonMark][]). That means [GitHub flavored markdown (GFM)][gfm] extensions are not supported by default. They can be enabled by using a remark plugin: [`remark-gfm`][remark-gfm]. -Such plugins can be passed in [`options.remarkPlugins`][options-remark-plugins]. +Such plugins can be passed in [`remarkPlugins` in +`ProcessorOptions`][processor-options]. More info on plugins is available in [§ Extending MDX][extend] Say we have an MDX file like this: @@ -121,6 +122,6 @@ console.log( [remark-gfm]: https://github.com/remarkjs/remark-gfm -[options-remark-plugins]: /packages/mdx/#optionsremarkplugins +[processor-options]: /packages/mdx/#processoroptions [extend]: /docs/extending-mdx/ diff --git a/docs/guides/math.mdx b/docs/guides/math.mdx index ae067b2fd..1d6678ba7 100644 --- a/docs/guides/math.mdx +++ b/docs/guides/math.mdx @@ -18,9 +18,8 @@ Math can be enabled by using a remark plugin: [`remark-math`][remark-math], combined with a rehype plugin: either [`rehype-katex`][rehype-katex] (KaTeX) or [`rehype-mathjax`][rehype-mathjax] (MathJax). -remark plugins can be passed in -[`options.remarkPlugins`][options-remark-plugins] and rehype -plugins in [`options.rehypePlugins`][options-rehype-plugins]. +Plugins can be passed in +[`rehypePlugins` and `remarkPlugins` in `ProcessorOptions`][processor-options]. More info on plugins is available in [§ Extending MDX][extend] Say we have an MDX file like this: @@ -93,9 +92,7 @@ console.log( [rehype-mathjax]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax -[options-remark-plugins]: /packages/mdx/#optionsremarkplugins - -[options-rehype-plugins]: /packages/mdx/#optionsrehypeplugins +[processor-options]: /packages/mdx/#processoroptions [extend]: /docs/extending-mdx/ diff --git a/docs/guides/mdx-on-demand.mdx b/docs/guides/mdx-on-demand.mdx index 33efee3f0..f4522c88d 100644 --- a/docs/guides/mdx-on-demand.mdx +++ b/docs/guides/mdx-on-demand.mdx @@ -105,6 +105,6 @@ export async function getStaticProps() { [compile]: /packages/mdx/#compilefile-options -[run]: /packages/mdx/#runfunctionbody-options +[run]: /packages/mdx/#runcode-options [eval]: /packages/mdx/#evaluatefile-options diff --git a/docs/migrating/v2.mdx b/docs/migrating/v2.mdx index b955939bb..3300f7e34 100644 --- a/docs/migrating/v2.mdx +++ b/docs/migrating/v2.mdx @@ -204,10 +204,8 @@ For more information, please see [§ API in `@mdx-js/loader`][loader-api]. The options accepted by the loader changed: -* `renderer` is replaced by - [`options.jsxImportSource`][mdx-options-jsximportsource], - [`options.providerImportSource`][mdx-options-providerimportsource], - and others options. +* `renderer` is replaced by `jsxImportSource`, `providerImportSource`, and + others options. More info in [§ API in `@mdx-js/mdx`][mdx-api] * Other options were undocumented but passed to `@mdx-js/mdx`. See `@mdx-js/mdx` below if needed @@ -326,13 +324,10 @@ Options in version 1 were undocumented. If you used them: * `filepath` is replaced by accepting a VFile or compatible object as the - first argument [`file`][mdx-file] -* `compilers` is replaced by - [`options.recmaPlugins`][mdx-options-recmaplugins] -* `hastPlugins` is replaced by - [`options.rehypePlugins`][mdx-options-rehypeplugins] -* `mdPlugins` is replaced by - [`options.remarkPlugins`][mdx-options-remarkplugins] + first argument `file` +* `compilers` is replaced by `recmaPlugins` +* `hastPlugins` is replaced by `rehypePlugins` +* `mdPlugins` is replaced by `options.remarkPlugins` * `skipExport` is removed, [`evaluate`][mdx-evaluate] can do this automatically * `wrapExport` is removed, use a custom recma plugin instead @@ -666,15 +661,15 @@ With our new AST, we’re able to create codemods from now on. [remark-mdx-use]: /packages/remark-mdx/#use -[mdx-compile]: /packages/mdx/#compile +[mdx-compile]: /packages/mdx/#compilefile-options -[mdx-compilesync]: /packages/mdx/#compilesync +[mdx-compilesync]: /packages/mdx/#compilesyncfile-options -[mdx-createprocessor]: /packages/mdx/#createprocessor +[mdx-createprocessor]: /packages/mdx/#createprocessoroptions -[mdx-evaluate]: /packages/mdx/#evaluate +[mdx-evaluate]: /packages/mdx/#evaluatefile-options -[mdx-evaluatesync]: /packages/mdx/#evaluatesync +[mdx-evaluatesync]: /packages/mdx/#evaluatesyncfile-options [mdx-react]: /packages/react/ @@ -686,18 +681,6 @@ With our new AST, we’re able to create codemods from now on. [vfile]: https://github.com/vfile/vfile -[mdx-file]: /packages/mdx/#file - -[mdx-options-recmaplugins]: /packages/mdx/#optionsrecmaplugins - -[mdx-options-remarkplugins]: /packages/mdx/#optionsremarkplugins - -[mdx-options-rehypeplugins]: /packages/mdx/#optionsrehypeplugins - -[mdx-options-jsximportsource]: /packages/mdx/#optionsjsximportsource - -[mdx-options-providerimportsource]: /packages/mdx/#optionsproviderimportsource - [babel-loader]: https://webpack.js.org/loaders/babel-loader/ [loader-api]: /packages/loader/#api diff --git a/package-lock.json b/package-lock.json index 78a70abc0..9c7c52b7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -873,9 +873,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -890,12 +890,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -917,9 +917,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -1344,9 +1344,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.7.1.tgz", - "integrity": "sha512-nIb8SOBgDEMFY2iS2MdnUZOg2ikcYchRrBoF+wtdjieRFKR2uGRipHY/oFLo+2N6anDualyClPzGywTHRGrLfw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.8.0.tgz", + "integrity": "sha512-TkRHIV6k2D8OlUe8RtG+5jgOF/H98Myx0M6AOafC8DdNVOFiBSFa5cpRDtpm8LXOa9sVwe0+e6Q3FC56X/DZfg==", "dev": true, "dependencies": { "debug": "4.3.4", @@ -1355,7 +1355,7 @@ "proxy-agent": "6.3.1", "tar-fs": "3.0.4", "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" + "yargs": "17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" @@ -1364,44 +1364,6 @@ "node": ">=16.3.0" } }, - "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@puppeteer/browsers/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@rollup/pluginutils": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz", @@ -1811,9 +1773,9 @@ "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" }, "node_modules/@types/react": { - "version": "18.2.29", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.29.tgz", - "integrity": "sha512-Z+ZrIRocWtdD70j45izShRwDuiB4JZqDegqMFW/I8aG5DxxLKOzVNoq62UIO82v9bdgi+DO1jvsb9sTEZUSm+Q==", + "version": "18.2.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.31.tgz", + "integrity": "sha512-c2UnPv548q+5DFh03y8lEDeMfDwBn9G3dRwfkrxQMo/dOtRHUUO57k6pHvBIfH/VF4Nh+98mZ5aaSe+2echD5g==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2237,106 +2199,106 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.4.tgz", - "integrity": "sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.6.tgz", + "integrity": "sha512-2JNjemwaNwf+MkkatATVZi7oAH1Hx0B04DdPH3ZoZ8vKC1xZVP7nl4HIsk8XYd3r+/52sqqoz9TWzYc3yE9dqA==", "dependencies": { - "@babel/parser": "^7.21.3", - "@vue/shared": "3.3.4", + "@babel/parser": "^7.23.0", + "@vue/shared": "3.3.6", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz", - "integrity": "sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.6.tgz", + "integrity": "sha512-1MxXcJYMHiTPexjLAJUkNs/Tw2eDf2tY3a0rL+LfuWyiKN2s6jvSwywH3PWD8bKICjfebX3GWx2Os8jkRDq3Ng==", "dependencies": { - "@vue/compiler-core": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-core": "3.3.6", + "@vue/shared": "3.3.6" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz", - "integrity": "sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==", - "dependencies": { - "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.4", - "@vue/compiler-dom": "3.3.4", - "@vue/compiler-ssr": "3.3.4", - "@vue/reactivity-transform": "3.3.4", - "@vue/shared": "3.3.4", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.6.tgz", + "integrity": "sha512-/Kms6du2h1VrXFreuZmlvQej8B1zenBqIohP0690IUBkJjsFvJxY0crcvVRJ0UhMgSR9dewB+khdR1DfbpArJA==", + "dependencies": { + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.6", + "@vue/compiler-dom": "3.3.6", + "@vue/compiler-ssr": "3.3.6", + "@vue/reactivity-transform": "3.3.6", + "@vue/shared": "3.3.6", "estree-walker": "^2.0.2", - "magic-string": "^0.30.0", - "postcss": "^8.1.10", + "magic-string": "^0.30.5", + "postcss": "^8.4.31", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz", - "integrity": "sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.6.tgz", + "integrity": "sha512-QTIHAfDCHhjXlYGkUg5KH7YwYtdUM1vcFl/FxFDlD6d0nXAmnjizka3HITp8DGudzHndv2PjKVS44vqqy0vP4w==", "dependencies": { - "@vue/compiler-dom": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-dom": "3.3.6", + "@vue/shared": "3.3.6" } }, "node_modules/@vue/reactivity": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.4.tgz", - "integrity": "sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.6.tgz", + "integrity": "sha512-gtChAumfQz5lSy5jZXfyXbKrIYPf9XEOrIr6rxwVyeWVjFhJwmwPLtV6Yis+M9onzX++I5AVE9j+iPH60U+B8Q==", "dependencies": { - "@vue/shared": "3.3.4" + "@vue/shared": "3.3.6" } }, "node_modules/@vue/reactivity-transform": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz", - "integrity": "sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.6.tgz", + "integrity": "sha512-RlJl4dHfeO7EuzU1iJOsrlqWyJfHTkJbvYz/IOJWqu8dlCNWtxWX377WI0VsbAgBizjwD+3ZjdnvSyyFW1YVng==", "dependencies": { - "@babel/parser": "^7.20.15", - "@vue/compiler-core": "3.3.4", - "@vue/shared": "3.3.4", + "@babel/parser": "^7.23.0", + "@vue/compiler-core": "3.3.6", + "@vue/shared": "3.3.6", "estree-walker": "^2.0.2", - "magic-string": "^0.30.0" + "magic-string": "^0.30.5" } }, "node_modules/@vue/runtime-core": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.4.tgz", - "integrity": "sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.6.tgz", + "integrity": "sha512-qp7HTP1iw1UW2ZGJ8L3zpqlngrBKvLsDAcq5lA6JvEXHmpoEmjKju7ahM9W2p/h51h0OT5F2fGlP/gMhHOmbUA==", "dependencies": { - "@vue/reactivity": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/reactivity": "3.3.6", + "@vue/shared": "3.3.6" } }, "node_modules/@vue/runtime-dom": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.4.tgz", - "integrity": "sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.6.tgz", + "integrity": "sha512-AoX3Cp8NqMXjLbIG9YR6n/pPLWE9TiDdk6wTJHFnl2GpHzDFH1HLBC9wlqqQ7RlnvN3bVLpzPGAAH00SAtOxHg==", "dependencies": { - "@vue/runtime-core": "3.3.4", - "@vue/shared": "3.3.4", - "csstype": "^3.1.1" + "@vue/runtime-core": "3.3.6", + "@vue/shared": "3.3.6", + "csstype": "^3.1.2" } }, "node_modules/@vue/server-renderer": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.4.tgz", - "integrity": "sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.6.tgz", + "integrity": "sha512-kgLoN43W4ERdZ6dpyy+gnk2ZHtcOaIr5Uc/WUP5DRwutgvluzu2pudsZGoD2b7AEJHByUVMa9k6Sho5lLRCykw==", "dependencies": { - "@vue/compiler-ssr": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-ssr": "3.3.6", + "@vue/shared": "3.3.6" }, "peerDependencies": { - "vue": "3.3.4" + "vue": "3.3.6" } }, "node_modules/@vue/shared": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.4.tgz", - "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==" + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.6.tgz", + "integrity": "sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==" }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", @@ -3240,13 +3202,14 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3411,9 +3374,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.4.31", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.31.tgz", - "integrity": "sha512-OtvEg2JMRQrHsmLx4FV3u1Hf9waYxB5PmL+yM0HkFpc9H2x3TMbUqS+GP2/fC4399hzOO+EQF8uVU43By9ILag==", + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.32.tgz", + "integrity": "sha512-RJnw0PW3sNdx1WclINVfVVx8JUH+tWTHZNpnEzlcM+Qgvf40dUH34U7gJq+cc/0LE+rbPxeT6ldqWrCbUf4jeg==", "dev": true, "dependencies": { "mitt": "3.0.1", @@ -4204,9 +4167,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1179426", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1179426.tgz", - "integrity": "sha512-KKC7IGwdOr7u9kTGgjUvGTov/z1s2H7oHi3zKCdR9eSDyCPia5CBi4aRhtp7d8uR7l0GS5UTDw3TjKGu5CqINg==", + "version": "0.0.1191157", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1191157.tgz", + "integrity": "sha512-Fu2mUhX7zkzLHMJZk5wQTiHdl1eJrhK0GypUoSzogUt51MmYEv/46pCz4PtGGFlr0f2ZyYDzzx5CPtbEkuvcTA==", "dev": true }, "node_modules/diff": { @@ -4329,9 +4292,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.559", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.559.tgz", - "integrity": "sha512-iS7KhLYCSJbdo3rUSkhDTVuFNCV34RKs2UaB9Ecr7VlqzjjWW//0nfsFF5dtDmyXlZQaDYYtID5fjtC/6lpRug==" + "version": "1.4.563", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.563.tgz", + "integrity": "sha512-dg5gj5qOgfZNkPNeyKBZQAQitIQ/xwfIDmEQJHCbXaD9ebTZxwJXUsDYcBlAvZGZLi+/354l35J1wkmP6CqYaw==" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -4406,26 +4369,26 @@ } }, "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -4435,7 +4398,7 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", @@ -4449,7 +4412,7 @@ "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -4486,26 +4449,26 @@ "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -4610,18 +4573,19 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -6194,15 +6158,15 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6476,12 +6440,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6526,6 +6490,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hast-util-embedded": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", @@ -6923,7 +6899,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.2.0.tgz", "integrity": "sha512-wSlp23N45CMjDg/BPW8zvhEi3R+8eRE1qFbjEyAUzMCzu2l1Wzwakq+Tlia9nkCtEl5mDxa7nKHsvYJ6Gfn21A==", - "dev": true, "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", @@ -7258,13 +7233,13 @@ "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -7466,12 +7441,12 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11108,9 +11083,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.0.tgz", - "integrity": "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12455,31 +12430,31 @@ } }, "node_modules/puppeteer": { - "version": "21.3.8", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.3.8.tgz", - "integrity": "sha512-4OrInVIAtDgcznENUV4Du4gYSZhRmbCkckvOoPstXrUH4JsQ3atSegY+9f/tOKCDB2qh7sXaszDcFEn+RymY0g==", + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.4.0.tgz", + "integrity": "sha512-KkiDe39NJxlw7fyiN6fieM9SVsewzt037nUZRoffNuFtYdAl5rRLVtleBuVZ5i1swK/R4CmA6Pbka/ytpFCu4Q==", "dev": true, "hasInstallScript": true, "dependencies": { - "@puppeteer/browsers": "1.7.1", + "@puppeteer/browsers": "1.8.0", "cosmiconfig": "8.3.6", - "puppeteer-core": "21.3.8" + "puppeteer-core": "21.4.0" }, "engines": { "node": ">=16.3.0" } }, "node_modules/puppeteer-core": { - "version": "21.3.8", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.3.8.tgz", - "integrity": "sha512-yv12E/+zZ7Lei5tJB4sUkSrsuqKibuYpYxLGbmtLUjjYIqGE5HKz9OUI2I/RFHEvF+pHi2bTbv5bWydeCGJ6Mw==", + "version": "21.4.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.4.0.tgz", + "integrity": "sha512-ONYjYgHItm6i9WdJf+MnRTRPB4HegwPbPfi1jjNN0LCW3rnNich/5hXgZFcn2oWvgFc8DWLDIcwly7C8z+EvIw==", "dev": true, "dependencies": { - "@puppeteer/browsers": "1.7.1", - "chromium-bidi": "0.4.31", + "@puppeteer/browsers": "1.8.0", + "chromium-bidi": "0.4.32", "cross-fetch": "4.0.0", "debug": "4.3.4", - "devtools-protocol": "0.0.1179426", + "devtools-protocol": "0.0.1191157", "ws": "8.14.2" }, "engines": { @@ -25840,6 +25815,21 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", @@ -26895,7 +26885,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -28705,15 +28695,23 @@ "dev": true }, "node_modules/vue": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.4.tgz", - "integrity": "sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.6.tgz", + "integrity": "sha512-jJIDETeWJnoY+gfn4ZtMPMS5KtbP4ax+CT4dcQFhTnWEk8xMupFyQ0JxL28nvT/M4+p4a0ptxaV2WY0LiIxvRg==", "dependencies": { - "@vue/compiler-dom": "3.3.4", - "@vue/compiler-sfc": "3.3.4", - "@vue/runtime-dom": "3.3.4", - "@vue/server-renderer": "3.3.4", - "@vue/shared": "3.3.4" + "@vue/compiler-dom": "3.3.6", + "@vue/compiler-sfc": "3.3.6", + "@vue/runtime-dom": "3.3.6", + "@vue/server-renderer": "3.3.6", + "@vue/shared": "3.3.6" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/walk-up-path": { @@ -28924,13 +28922,13 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" @@ -29512,6 +29510,7 @@ "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.0", "hast-util-to-estree": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "periscopic": "^3.0.0", "remark-mdx": "^2.0.0", diff --git a/packages/esbuild/lib/index.js b/packages/esbuild/lib/index.js index cdc3dc8d5..83ccc580a 100644 --- a/packages/esbuild/lib/index.js +++ b/packages/esbuild/lib/index.js @@ -14,7 +14,11 @@ * @typedef EsbuildOptions * Extra options. * @property {boolean | null | undefined} [allowDangerousRemoteMdx=false] - * Allow remote MDX (default: `false`). + * Whether to allow importing from `http:` and `https:` URLs (`boolean`, + * default: `false`). + * + * When passing `allowDangerousRemoteMdx`, MD(X) *and* JS files can be + * imported from `http:` and `https:` urls. * * @typedef {Omit & LoadDataFields} LoadData * Data passed to `onload`. @@ -27,6 +31,20 @@ * @typedef {EsbuildOptions & ProcessorOptions} Options * Configuration. * + * Options are the same as `compile` from `@mdx-js/mdx` with the addition + * of `allowDangerousRemoteMdx`. + * + * ###### Notes + * + * > ⚠️ **Security**: `allowDangerousRemoteMdx` (intentionally) enabled remote + * > code execution. + * > Make sure you trust your code! + * > See [§ Security][security] for more + * > info. + * + * > 💡 **Experiment**: `allowDangerousRemoteMdx` is an experimental feature + * > that might not work well and might change in minor releases. + * * @typedef PluginData * Extra data passed. * @property {Buffer | string | null | undefined} [contents] @@ -60,7 +78,13 @@ const p = process const remoteNamespace = name + '-remote' /** - * Compile MDX w/ esbuild. + * Create an esbuild plugin to compile MDX to JS. + * + * esbuild takes care of turning modern JavaScript features into syntax that + * works wherever you want it to. + * With other integrations you might need to use Babel for this, but with + * esbuild that’s not needed. + * See esbuild’s docs for more info. * * @param {Readonly | null | undefined} [options] * Configuration (optional). diff --git a/packages/esbuild/readme.md b/packages/esbuild/readme.md index 3b6fd0752..1bd97b534 100644 --- a/packages/esbuild/readme.md +++ b/packages/esbuild/readme.md @@ -41,19 +41,11 @@ used, or our webpack loader (`@mdx-js/loader`) or Rollup plugin ## Install -This package is [ESM only][esm]: -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh -npm install @mdx-js/esbuild -``` - -[yarn][]: - -```sh -yarn add @mdx-js/esbuild +npm install @mdx-js/esbuild-mdx ``` ## Use @@ -61,21 +53,26 @@ yarn add @mdx-js/esbuild Do something like this with the esbuild API: ```tsx -import esbuild from 'esbuild' import mdx from '@mdx-js/esbuild' +import esbuild from 'esbuild' await esbuild.build({ - entryPoints: ['index.mdx'], + // Replace `index.js` with your entry point that imports MDX files: + entryPoints: ['index.js'], format: 'esm', outfile: 'output.js', - plugins: [mdx({/* Options… */})] + plugins: [ + mdx({ + /* Options… */ + }) + ] }) ``` ## API -This package exports a function as the default export that returns an -[esbuild][] plugin. +This package exports no identifiers. +The default export is [`mdx`][api-mdx]. ### `mdx(options?)` @@ -87,30 +84,47 @@ With other integrations you might need to use Babel for this, but with esbuild that’s not needed. See esbuild’s docs for more info. -###### `options` +###### Parameters + +* `options` ([`Options`][api-options], optional) + — configuration + +###### Returns + +ESBuild plugin ([`Plugin`][esbuild-plugin] from `esbuild`). + +### `Options` + +Configuration (TypeScript type). -`options` are the same as -[`compile` from `@mdx-js/mdx`][options] -with the addition of: +Options are the same as [`CompileOptions` from `@mdx-js/mdx`][compile-options] +with the addition of `allowDangerousRemoteMdx`: -###### `options.allowDangerousRemoteMdx` +###### Fields -> ⚠️ **Security**: this includes remote code in your bundle. -> Make sure you trust it! +* `allowDangerousRemoteMdx` (`boolean`, default: `false`) + — whether to allow importing from `http:` and `https:` URLs; + when passing `allowDangerousRemoteMdx`, MD(X) *and* JS files can be imported + from `http:` and `https:` urls; + +###### Notes + +> ⚠️ **Security**: `allowDangerousRemoteMdx` (intentionally) enabled remote +> code execution. +> Make sure you trust your code! > See [§ Security][security] for more > info. -> 💡 **Experiment**: this is an experimental feature that might not work -> well and might change in minor releases. +> 💡 **Experiment**: `allowDangerousRemoteMdx` is an experimental feature that +> might not work well and might change in minor releases. -Whether to allow importing from `http:` and `https:` URLs (`boolean`, default: -`false`). +## Examples + +### Use `allowDangerousRemoteMdx` -When passing `allowDangerousRemoteMdx`, MD(X) and JS files can be imported from -`http:` and `https:` urls. Take this `index.mdx` file: -```tsx +```mdx import Readme from 'https://raw.githubusercontent.com/mdx-js/mdx/main/readme.md' Here’s the readme: @@ -118,11 +132,11 @@ Here’s the readme: ``` -And a module `build.js`: +…and a module `build.js`: ```tsx -import esbuild from 'esbuild' import mdx from '@mdx-js/esbuild' +import esbuild from 'esbuild' await esbuild.build({ entryPoints: ['index.mdx'], @@ -132,8 +146,8 @@ await esbuild.build({ }) ``` -Running that (`node build.js`) and evaluating `output.js` (depends on how you -evaluate React stuff) would give: +…then running that (`node build.js`) and evaluating `output.js` (depends on how +you evaluate React or another framework) would give: ```tsx

Here’s the readme:

@@ -145,10 +159,9 @@ evaluate React stuff) would give: ## Types This package is fully typed with [TypeScript][]. +It exports the additional type [`Options`][api-options]. See [§ Types][types] on our website for information. -An `Options` type is exported, which represents acceptable configuration. - ## Security See [§ Security][security] on our website for information. @@ -190,8 +203,6 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[yarn]: https://classic.yarnpkg.com/docs/cli/add/ - [contribute]: https://mdxjs.com/community/contribute/ [support]: https://mdxjs.com/community/support/ @@ -210,6 +221,12 @@ abide by its terms. [security]: https://mdxjs.com/getting-started/#security -[options]: https://mdxjs.com/packages/mdx/#compilefile-options - [typescript]: https://www.typescriptlang.org + +[compile-options]: https://mdxjs.com/packages/mdx/#compileoptions + +[esbuild-plugin]: https://esbuild.github.io/plugins/ + +[api-mdx]: #mdxoptions + +[api-options]: #options diff --git a/packages/loader/index.cjs b/packages/loader/index.cjs index 6ea57decf..a5a1e9201 100644 --- a/packages/loader/index.cjs +++ b/packages/loader/index.cjs @@ -2,6 +2,9 @@ * @typedef {import('webpack').LoaderContext} LoaderContext */ +// @ts-expect-error: TS complains about CJS importing ESM but it works. +/** @typedef {import('./lib/index.js').Options} Options */ + 'use strict' /** diff --git a/packages/loader/lib/index.js b/packages/loader/lib/index.js index eaff38a91..fb4656fde 100644 --- a/packages/loader/lib/index.js +++ b/packages/loader/lib/index.js @@ -8,10 +8,13 @@ */ /** - * @typedef {Pick} Defaults - * Defaults. - * @typedef {Omit} Options - * Configuration. + * @typedef {Omit} Options + * Configuration (TypeScript type). + * + * Options are the same as `compile` from `@mdx-js/mdx` with the exception + * that the `SourceMapGenerator` and `development` options are supported + * based on how you configure webpack. + * You cannot pass them manually. * * @callback Process * Process. @@ -48,14 +51,13 @@ const cache = new WeakMap() * Nothing. */ export function loader(value, callback) { - /** @type {Defaults} */ - const defaults = this.sourceMap ? {SourceMapGenerator} : {} - const options = { + const options = /** @type {CompileOptions} */ (this.getOptions()) + const hash = getOptionsHash(options) + const config = { + SourceMapGenerator: this.sourceMap ? SourceMapGenerator : undefined, development: this.mode === 'development', - .../** @type {CompileOptions} */ (this.getOptions()) + ...options } - const config = {...defaults, ...options} - const hash = getOptionsHash(options) /* c8 ignore next -- some loaders set `undefined` (see `TypeStrong/ts-loader`). */ const compiler = this._compiler || marker diff --git a/packages/loader/readme.md b/packages/loader/readme.md index 3bf059b6e..818c8b379 100644 --- a/packages/loader/readme.md +++ b/packages/loader/readme.md @@ -32,36 +32,28 @@ This package is a webpack loader to support MDX. This integration is useful if you’re using webpack (or another tool that uses webpack, such as Next.js). -This integration can be combined with the Babel loader to support nonstandard -JSX runtimes (such as Vue) or compile modern JavaScript features to ones your -users support. +This integration can be combined with the Babel loader to compile modern +JavaScript features to ones your users support. If you want to evaluate MDX code then the lower-level compiler (`@mdx-js/mdx`) can be used manually. ## Install -This package is [ESM only][esm]: -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install @mdx-js/loader ``` -[yarn][]: - -```sh -yarn add @mdx-js/loader -``` - ## Use Add something along these lines to your `webpack.config.js`: ```tsx -module.exports = { +/** @type {import('webpack').Configuration} */ +const webpackConfig = { module: { // … rules: [ @@ -79,49 +71,73 @@ module.exports = { ] } } + +export default webpackConfig ``` -See also [¶ Create React App (CRA)][cra], [¶ Next.js][next], and -[¶ Vue CLI][vue-cli], if you’re using webpack through them, for more info. +See also [¶ Next.js][next] and [¶ Vue CLI][vue-cli], if you’re using webpack +through them, for more info. ## API +This package exports no identifiers. +The default export is [`mdx`][api-mdx]. + +### `mdx` + This package exports a [webpack][] plugin as the default export. +Configuration (see [`Options`][api-options]) are passed separately through +webpack. -Source maps are supported based on how you configure webpack. -You do not need to pass `options.SourceMapGenerator`. +### `Options` -###### `options` +Configuration (TypeScript type). -`options` are the same as [`compile` from `@mdx-js/mdx`][options]. +Options are the same as [`CompileOptions` from `@mdx-js/mdx`][compile-options] +with the exception that the `SourceMapGenerator` and `development` options are +supported based on how you configure webpack. +You cannot pass them manually. -###### Note: Babel +## Examples + +### Combine with Babel If you use modern JavaScript features you might want to use Babel through -[`babel-loader`][babel-loader] to compile to code that works: +[`babel-loader`][babel-loader] to compile to code that works in older browsers: ```tsx -// … -use: [ - // Note that Webpack runs right-to-left: `@mdx-js/loader` is used first, then - // `babel-loader`. - {loader: 'babel-loader', options: {}}, - { - loader: '@mdx-js/loader', - /** @type {import('@mdx-js/loader').Options} */ - options: {}, - }, -]; -// … +/** @type {import('webpack').Configuration} */ +const webpackConfig = { + module: { + // … + rules: [ + // … + { + test: /\.mdx?$/, + use: [ + // Note that Webpack runs right-to-left: `@mdx-js/loader` is used first, then + // `babel-loader`. + {loader: 'babel-loader', options: {}}, + { + loader: '@mdx-js/loader', + /** @type {import('@mdx-js/loader').Options} */ + options: {} + } + ] + } + ] + } +} + +export default webpackConfig ``` ## Types This package is fully typed with [TypeScript][]. +It exports the additional type [`Options`][api-options]. See [§ Types][types] on our website for information. -An `Options` type is exported, which represents acceptable configuration. - ## Security See [§ Security][security] on our website for information. @@ -163,8 +179,6 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[yarn]: https://classic.yarnpkg.com/docs/cli/add/ - [contribute]: https://mdxjs.com/community/contribute/ [support]: https://mdxjs.com/community/support/ @@ -175,22 +189,24 @@ abide by its terms. [vercel]: https://vercel.com -[webpack]: https://webpack.js.org - [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c +[security]: https://mdxjs.com/getting-started/#security + [types]: https://mdxjs.com/getting-started/#types -[security]: https://mdxjs.com/getting-started/#security +[webpack]: https://webpack.js.org -[options]: https://mdxjs.com/packages/mdx/#compilefile-options +[compile-options]: https://mdxjs.com/packages/mdx/#compileoptions [typescript]: https://www.typescriptlang.org [babel-loader]: https://webpack.js.org/loaders/babel-loader/ -[cra]: https://mdxjs.com/getting-started/#create-react-app-cra - [next]: https://mdxjs.com/getting-started/#nextjs [vue-cli]: https://mdxjs.com/getting-started/#vue-cli + +[api-mdx]: #mdx + +[api-options]: #options diff --git a/packages/mdx/index.js b/packages/mdx/index.js index 8cb5ce837..3e35619fe 100644 --- a/packages/mdx/index.js +++ b/packages/mdx/index.js @@ -1,7 +1,12 @@ /** + * @typedef {import('./lib/util/resolve-evaluate-options.js').Fragment} Fragment + * @typedef {import('./lib/util/resolve-evaluate-options.js').Jsx} Jsx + * @typedef {import('./lib/util/resolve-evaluate-options.js').JsxDev} JsxDev + * @typedef {import('./lib/util/resolve-evaluate-options.js').UseMdxComponents} UseMdxComponents * @typedef {import('./lib/compile.js').CompileOptions} CompileOptions * @typedef {import('./lib/core.js').ProcessorOptions} ProcessorOptions * @typedef {import('./lib/evaluate.js').EvaluateOptions} EvaluateOptions + * @typedef {import('./lib/run.js').RunOptions} RunOptions */ export {compile, compileSync} from './lib/compile.js' diff --git a/packages/mdx/lib/compile.js b/packages/mdx/lib/compile.js index f591a955f..a4fa54adc 100644 --- a/packages/mdx/lib/compile.js +++ b/packages/mdx/lib/compile.js @@ -1,12 +1,11 @@ /** * @typedef {import('vfile').VFile} VFile * @typedef {import('vfile').Compatible} Compatible - * @typedef {import('./core.js').BaseProcessorOptions} BaseProcessorOptions - * @typedef {import('./core.js').PluginOptions} PluginOptions + * @typedef {import('./core.js').ProcessorOptions} ProcessorOptions */ /** - * @typedef {Omit} CoreProcessorOptions + * @typedef {Omit} CoreProcessorOptions * Core configuration. * * @typedef ExtraOptions @@ -14,8 +13,13 @@ * @property {'detect' | 'md' | 'mdx' | null | undefined} [format='detect'] * Format of `file` (default: `'detect'`). * - * @typedef {CoreProcessorOptions & ExtraOptions & PluginOptions} CompileOptions - * Configuration. + * @typedef {CoreProcessorOptions & ExtraOptions} CompileOptions + * Configuration for `compile`. + * + * `CompileOptions` is the same as `ProcessorOptions` with the exception that + * the `format` option supports a `'detect'` value, which is the default. + * The `'detect'` format means to use `'md'` for files with an extension in + * `mdExtensions` and `'mdx'` otherwise. */ import {resolveFileAndOptions} from './util/resolve-file-and-options.js' @@ -25,12 +29,11 @@ import {createProcessor} from './core.js' * Compile MDX to JS. * * @param {Readonly} vfileCompatible - * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be - * given to `vfile`). + * MDX document to parse. * @param {Readonly | null | undefined} [compileOptions] * Compile configuration (optional). * @return {Promise} - * File. + * Promise to compiled file. */ export function compile(vfileCompatible, compileOptions) { const {file, options} = resolveFileAndOptions(vfileCompatible, compileOptions) @@ -40,13 +43,14 @@ export function compile(vfileCompatible, compileOptions) { /** * Synchronously compile MDX to JS. * + * When possible please use the async `compile`. + * * @param {Readonly} vfileCompatible - * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be - * given to `vfile`). + * MDX document to parse. * @param {Readonly | null | undefined} [compileOptions] * Compile configuration (optional). * @return {VFile} - * File. + * Compiled file. */ export function compileSync(vfileCompatible, compileOptions) { const {file, options} = resolveFileAndOptions(vfileCompatible, compileOptions) diff --git a/packages/mdx/lib/core.js b/packages/mdx/lib/core.js index 7e42f9542..ca888ae35 100644 --- a/packages/mdx/lib/core.js +++ b/packages/mdx/lib/core.js @@ -1,43 +1,128 @@ /** * @typedef {import('estree-jsx').Program} Program + * @typedef {import('hast-util-to-estree').ElementAttributeNameCase} ElementAttributeNameCase + * @typedef {import('hast-util-to-estree').StylePropertyNameCase} StylePropertyNameCase * @typedef {import('mdast').Root} Root * @typedef {import('remark-rehype').Options} RemarkRehypeOptions + * @typedef {typeof import('source-map').SourceMapGenerator} SourceMapGenerator * @typedef {import('unified').PluggableList} PluggableList * @typedef {import('unified').Processor} Processor - * @typedef {import('./plugin/recma-document.js').Options} RecmaDocumentOptions - * @typedef {import('./plugin/recma-jsx-rewrite.js').Options} RecmaJsxRewriteOptions - * @typedef {import('./plugin/recma-stringify.js').Options} RecmaStringifyOptions - * @typedef {import('./plugin/rehype-recma.js').Options} RehypeRecmaOptions */ /** - * @typedef BaseProcessorOptions - * Base configuration. - * @property {boolean | null | undefined} [jsx=false] - * Whether to keep JSX (default: `false`). + * @typedef ProcessorOptions + * Configuration for `createProcessor`. + * @property {SourceMapGenerator | null | undefined} [SourceMapGenerator] + * Add a source map (object form) as the `map` field on the resulting file + * (optional). + * @property {string | null | undefined} [baseUrl] + * Resolve `import`s (and `export … from`, and `import.meta.url`) from this + * URL (optional, example: `import.meta.url`); + * this option is useful when code will run in a different place, such as + * when `.mdx` files are in path *a* but compiled to path *b* and imports + * should run relative the path *b*, or when evaluating code, whether in Node + * or a browser. + * @property {boolean | null | undefined} [development=false] + * Whether to add extra info to error messages in generated code and use the + * development automatic JSX runtime (`Fragment` and `jsxDEV` from + * `/jsx-dev-runtime`) (default: `false`); + * when using the webpack loader (`@mdx-js/loader`) or the Rollup integration + * (`@mdx-js/rollup`) through Vite, this is automatically inferred from how + * you configure those tools. + * @property {ElementAttributeNameCase | null | undefined} [elementAttributeNameCase='react'] + * Casing to use for attribute names (default: `'react'`); + * HTML casing is for example `class`, `stroke-linecap`, `xml:lang`; + * React casing is for example `className`, `strokeLinecap`, `xmlLang`; + * for JSX components written in MDX, the author has to be aware of which + * framework they use and write code accordingly; + * for AST nodes generated by this project, this option configures it * @property {'md' | 'mdx' | null | undefined} [format='mdx'] - * Format of the files to be processed (default: `'mdx'`). - * @property {'function-body' | 'program'} [outputFormat='program'] - * Whether to compile to a whole program or a function body (default: - * `'program'`). + * format of the file (default: `'mdx'`); + * `'md'` means treat as markdown and `'mdx'` means treat as MDX. + * @property {boolean | null | undefined} [jsx=false] + * Whether to keep JSX (default: `false`); + * the default is to compile JSX away so that the resulting file is + * immediately runnable. + * @property {string | null | undefined} [jsxImportSource='react'] + * Place to import automatic JSX runtimes from (default: `'react'`); + * when in the `automatic` runtime, this is used to define an import for + * `Fragment`, `jsx`, `jsxDEV`, and `jsxs`. + * @property {'automatic' | 'classic' | null | undefined} [jsxRuntime='automatic'] + * JSX runtime to use (default: `'automatic'`); + * the automatic runtime compiles to `import _jsx from + * '$importSource/jsx-runtime'\n_jsx('p')`; + * the classic runtime compiles to calls such as `h('p')`. * @property {ReadonlyArray | null | undefined} [mdExtensions] - * Extensions (with `.`) for markdown (default: `['.md', '.markdown', …]`). + * List of markdown extensions, with dot (default: `['.md', '.markdown', …]`); + * affects integrations. * @property {ReadonlyArray | null | undefined} [mdxExtensions] - * Extensions (with `.`) for MDX (default: `['.mdx']`). + * List of MDX extensions, with dot (default: `['.mdx']`); + * affects integrations. + * @property {'function-body' | 'program' | null | undefined} [outputFormat='program'] + * Output format to generate (default: `'program'`); + * in most cases `'program'` should be used, it results in a whole program; + * internally `evaluate` uses `'function-body'` to compile to + * code that can be passed to `run`; + * in some cases, you might want to do what `evaluate` does in separate steps + * yourself, such as when compiling on the server and running on the client. + * @property {string | null | undefined} [pragma='React.createElement'] + * Pragma for JSX, used in the classic runtime as an identifier for function + * calls: `` to `React.createElement('x')` (default: + * `'React.createElement'`); + * when changing this, you should also define `pragmaFrag` and + * `pragmaImportSource` too. + * @property {string | null | undefined} [pragmaFrag='React.Fragment'] + * Pragma for fragment symbol, used in the classic runtime as an identifier + * for unnamed calls: `<>` to `React.createElement(React.Fragment)` (default: + * `'React.Fragment'`); + * when changing this, you should also define `pragma` and + * `pragmaImportSource` too. + * @property {string | null | undefined} [pragmaImportSource='react'] + * Where to import the identifier of `pragma` from, used in the classic + * runtime (default: `'react'`); + * to illustrate, when `pragma` is `'a.b'` and `pragmaImportSource` is `'c'` + * the following will be generated: `import a from 'c'` and things such as + * `a.b('h1', {})`. + * when changing this, you should also define `pragma` and `pragmaFrag` too. + * @property {string | null | undefined} [providerImportSource] + * Place to import a provider from (optional, example: `'@mdx-js/react'`); + * normally it’s used for runtimes that support context (React, Preact), but + * it can be used to inject components into the compiled code; + * the module must export and identifier `useMDXComponents` which is called + * without arguments to get an object of components (`MDXComponents` from + * `mdx/types.js`). * @property {PluggableList | null | undefined} [recmaPlugins] - * List of recma (esast, JavaScript) plugins (optional). + * List of recma plugins (optional); + * this is a new ecosystem, currently in beta, to transform esast trees + * (JavaScript) * @property {PluggableList | null | undefined} [remarkPlugins] - * List of remark (mdast, markdown) plugins (optional). + * List of remark plugins (optional). * @property {PluggableList | null | undefined} [rehypePlugins] - * List of rehype (hast, HTML) plugins (optional). + * List of rehype plugins (optional). * @property {Readonly | null | undefined} [remarkRehypeOptions] - * Options to pass through to `remark-rehype` (optional). - * - * @typedef {Omit} PluginOptions - * Configuration for internal plugins. - * - * @typedef {BaseProcessorOptions & PluginOptions} ProcessorOptions - * Configuration for processor. + * Options to pass through to `remark-rehype` (optional); + * the option `allowDangerousHtml` will always be set to `true` and the MDX + * nodes (see `nodeTypes`) are passed through; + * In particular, you might want to pass configuration for footnotes if your + * content is not in English. + * @property {StylePropertyNameCase | null | undefined} [stylePropertyNameCase='dom'] + * Casing to use for property names in `style` objects (default: `'dom'`); + * CSS casing is for example `background-color` and `-webkit-line-clamp`; + * DOM casing is for example `backgroundColor` and `WebkitLineClamp`; + * for JSX components written in MDX, the author has to be aware of which + * framework they use and write code accordingly; + * for AST nodes generated by this project, this option configures it + * @property {boolean | null | undefined} [tableCellAlignToStyle=true] + * Turn obsolete `align` props on `td` and `th` into CSS `style` props + * (default: `true`). + * @property {boolean | null | undefined} [useDynamicImport=false] + * whether to compile to dynamic import expressions when `outputFormat` is + * `'function-body'` (default: `false`); + * so, it will turn import statements (`import {x} from 'y'`) into dynamic + * import expressions (`const {x} = await import('y')`); + * import statements only work at the top level of modules but import + * expressions are available inside function bodies; + * you should probably set `baseUrl` too. */ import remarkMdx from 'remark-mdx' @@ -63,11 +148,9 @@ const removedOptions = [ ] /** - * Pipeline to: + * Create a processor to compile markdown or MDX to JavaScript. * - * 1. Parse MDX (serialized markdown with embedded JSX, ESM, and expressions) - * 2. Transform through remark (mdast), rehype (hast), and recma (esast) - * 3. Serialize as JavaScript + * > **Note**: `format: 'detect'` is not allowed in `ProcessorOptions`. * * @param {Readonly | null | undefined} [options] * Configuration (optional). diff --git a/packages/mdx/lib/evaluate.js b/packages/mdx/lib/evaluate.js index 568ca7f26..95459a4e7 100644 --- a/packages/mdx/lib/evaluate.js +++ b/packages/mdx/lib/evaluate.js @@ -9,33 +9,61 @@ import {compile, compileSync} from './compile.js' import {run, runSync} from './run.js' /** - * Evaluate MDX. + * Compile and run MDX. * - * @param {Readonly} vfileCompatible - * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be - * given to `vfile`). - * @param {Readonly} evaluateOptions - * Configuration for evaluation. + * When you trust your content, `evaluate` can work. + * When possible, use `compile`, write to a file, and then run with Node or use + * one of the integrations. + * + * > ☢️ **Danger**: it’s called **evaluate** because it `eval`s JavaScript. + * + * ###### Notes + * + * Compiling (and running) MDX takes time. + * + * If you are live-rendering a string of MDX that often changes using a virtual + * DOM based framework (such as React), one performance improvement is to call + * the `MDXContent` component yourself. + * The reason is that the `evaluate` creates a new function each time, which + * cannot be diffed: + * + * ```diff + * const {default: MDXContent} = await evaluate('…') + * + * - + * +MDXContent(props) + * ``` + * + * @param {Readonly} file + * MDX document to parse. + * @param {Readonly} options + * Configuration (**required**). * @return {Promise} - * Export map. + * Promise to a module; + * the result is an object with a `default` field set to the component; + * anything else that was exported is available too. + */ -export async function evaluate(vfileCompatible, evaluateOptions) { - const {compiletime, runtime} = resolveEvaluateOptions(evaluateOptions) - return run(await compile(vfileCompatible, compiletime), runtime) +export async function evaluate(file, options) { + const {compiletime, runtime} = resolveEvaluateOptions(options) + return run(await compile(file, compiletime), runtime) } /** - * Synchronously evaluate MDX. + * Compile and run MDX, synchronously. + * + * When possible please use the async `evaluate`. + * + * > ☢️ **Danger**: it’s called **evaluate** because it `eval`s JavaScript. * - * @param {Readonly} vfileCompatible - * MDX document to parse (`string`, `Buffer`, `vfile`, anything that can be - * given to `vfile`). - * @param {Readonly} evaluateOptions - * Configuration for evaluation. + * @param {Readonly} file + * MDX document to parse. + * @param {Readonly} options + * Configuration (**required**). * @return {MDXModule} - * Export map. + * Module. */ -export function evaluateSync(vfileCompatible, evaluateOptions) { - const {compiletime, runtime} = resolveEvaluateOptions(evaluateOptions) - return runSync(compileSync(vfileCompatible, compiletime), runtime) +export function evaluateSync(file, options) { + const {compiletime, runtime} = resolveEvaluateOptions(options) + return runSync(compileSync(file, compiletime), runtime) } diff --git a/packages/mdx/lib/plugin/recma-document.js b/packages/mdx/lib/plugin/recma-document.js index 2777d975b..a25e0009c 100644 --- a/packages/mdx/lib/plugin/recma-document.js +++ b/packages/mdx/lib/plugin/recma-document.js @@ -21,36 +21,10 @@ * @typedef {import('estree-jsx').SpreadElement} SpreadElement * @typedef {import('estree-jsx').Statement} Statement * @typedef {import('estree-jsx').VariableDeclarator} VariableDeclarator + * * @typedef {import('vfile').VFile} VFile - */ - -/** - * @typedef Options - * Configuration for internal plugin `recma-document`. - * @property {'function-body' | 'program' | null | undefined} [outputFormat='program'] - * Whether to use either `import` and `export` statements to get the runtime - * (and optionally provider) and export the content, or get values from - * `arguments` and return things (default: `'program'`). - * @property {boolean | null | undefined} [useDynamicImport=false] - * Whether to keep `import` (and `export … from`) statements or compile them - * to dynamic `import()` instead (default: `false`). - * @property {string | null | undefined} [baseUrl] - * Resolve `import`s (and `export … from`, and `import.meta.url`) relative to - * this URL (optional). - * @property {string | null | undefined} [pragma='React.createElement'] - * Pragma for JSX (used in classic runtime) (default: - * `'React.createElement'`). - * @property {string | null | undefined} [pragmaFrag='React.Fragment'] - * Pragma for JSX fragments (used in classic runtime) (default: - * `'React.Fragment'`). - * @property {string | null | undefined} [pragmaImportSource='react'] - * Where to import the identifier of `pragma` from (used in classic runtime) - * (default: `'react'`). - * @property {string | null | undefined} [jsxImportSource='react'] - * Place to import automatic JSX runtimes from (used in automatic runtime) - * (default: `'react'`). - * @property {'automatic' | 'classic' | null | undefined} [jsxRuntime='automatic'] - * JSX runtime to use (default: `'automatic'`). + * + * @typedef {import('../core.js').ProcessorOptions} ProcessorOptions */ import {ok as assert} from 'devlop' @@ -66,24 +40,22 @@ import {specifiersToDeclarations} from '../util/estree-util-specifiers-to-declar /** * Wrap the estree in `MDXContent`. * - * @param {Readonly | null | undefined} [options] - * Configuration (optional). + * @param {Readonly} options + * Configuration. * @returns * Transform. */ export function recmaDocument(options) { - /* c8 ignore next -- always given in `@mdx-js/mdx` */ - const options_ = options || {} - const baseUrl = options_.baseUrl || undefined - const useDynamicImport = options_.useDynamicImport || undefined - const outputFormat = options_.outputFormat || 'program' + const baseUrl = options.baseUrl || undefined + const useDynamicImport = options.useDynamicImport || undefined + const outputFormat = options.outputFormat || 'program' const pragma = - options_.pragma === undefined ? 'React.createElement' : options_.pragma + options.pragma === undefined ? 'React.createElement' : options.pragma const pragmaFrag = - options_.pragmaFrag === undefined ? 'React.Fragment' : options_.pragmaFrag - const pragmaImportSource = options_.pragmaImportSource || 'react' - const jsxImportSource = options_.jsxImportSource || 'react' - const jsxRuntime = options_.jsxRuntime || 'automatic' + options.pragmaFrag === undefined ? 'React.Fragment' : options.pragmaFrag + const pragmaImportSource = options.pragmaImportSource || 'react' + const jsxImportSource = options.jsxImportSource || 'react' + const jsxRuntime = options.jsxRuntime || 'automatic' /** * @param {Program} tree diff --git a/packages/mdx/lib/plugin/recma-jsx-rewrite.js b/packages/mdx/lib/plugin/recma-jsx-rewrite.js index e2bd63846..db93bf60e 100644 --- a/packages/mdx/lib/plugin/recma-jsx-rewrite.js +++ b/packages/mdx/lib/plugin/recma-jsx-rewrite.js @@ -16,24 +16,11 @@ * @typedef {import('periscopic').Scope} PeriscopicScope * * @typedef {import('vfile').VFile} VFile + * + * @typedef {import('../core.js').ProcessorOptions} ProcessorOptions */ /** - * @typedef Options - * Configuration for internal plugin `recma-jsx-rewrite`. - * @property {'function-body' | 'program' | null | undefined} [outputFormat='program'] - * Whether to use an import statement or `arguments[0]` to get the provider. - * @property {string | null | undefined} [providerImportSource] - * Place to import a provider from. - * @property {boolean | null | undefined} [development=false] - * Whether to add extra info to error messages in generated code. - * - * This also results in the development automatic JSX runtime - * (`/jsx-dev-runtime`, `jsxDEV`) being used, which passes positional info to - * nodes. - * The default can be set to `true` in Node.js through environment variables: - * set `NODE_ENV=development`. - * * @typedef {PeriscopicScope & {node: Node}} Scope * Scope (with a `node`). * @@ -72,14 +59,13 @@ import { * It also makes sure that any undefined components are defined: either from * received components or as a function that throws an error. * - * @param {Readonly | null | undefined} [options] + * @param {Readonly} options * Configuration (optional). * @returns * Transform. */ export function recmaJsxRewrite(options) { - /* c8 ignore next -- always given in `@mdx-js/mdx` */ - const {development, outputFormat, providerImportSource} = options || {} + const {development, outputFormat, providerImportSource} = options /** * @param {Program} tree @@ -580,7 +566,7 @@ export function recmaJsxRewrite(options) { /** * @param {string} providerImportSource * Provider source. - * @param {Options['outputFormat']} outputFormat + * @param {'function-body' | 'program' | null | undefined} outputFormat * Format. * @returns {ModuleDeclaration | Statement} * Node. diff --git a/packages/mdx/lib/plugin/recma-stringify.js b/packages/mdx/lib/plugin/recma-stringify.js index 6c0e811ba..d9630f0bd 100644 --- a/packages/mdx/lib/plugin/recma-stringify.js +++ b/packages/mdx/lib/plugin/recma-stringify.js @@ -1,16 +1,9 @@ /** * @typedef {import('estree-jsx').Program} Program - * @typedef {typeof import('source-map').SourceMapGenerator} SourceMapGenerator * @typedef {import('unified').Processor} Processor * @typedef {import('vfile').VFile} VFile - */ - -/** - * @typedef Options - * Configuration for internal plugin `recma-stringify`. - * @property {SourceMapGenerator | null | undefined} [SourceMapGenerator] - * Generate a source map by passing a `SourceMapGenerator` from `source-map` - * in (optional). + * + * @typedef {import('../core.js').ProcessorOptions} ProcessorOptions */ import {jsx, toJs} from 'estree-util-to-js' @@ -18,15 +11,13 @@ import {jsx, toJs} from 'estree-util-to-js' /** * Serialize an esast (estree) program to JavaScript. * - * @type {import('unified').Plugin<[Options | null | undefined] | [], Program, string>} - * Plugin. + * @type {import('unified').Plugin<[Readonly], Program, string>} */ export function recmaStringify(options) { // @ts-expect-error: TS is wrong about `this`. // eslint-disable-next-line unicorn/no-this-assignment const self = /** @type {Processor} */ (this) - /* c8 ignore next -- always given in `@mdx-js/mdx` */ - const {SourceMapGenerator} = options || {} + const {SourceMapGenerator} = options self.compiler = compiler diff --git a/packages/mdx/lib/plugin/rehype-recma.js b/packages/mdx/lib/plugin/rehype-recma.js index e2a6ffd89..cb4d87220 100644 --- a/packages/mdx/lib/plugin/rehype-recma.js +++ b/packages/mdx/lib/plugin/rehype-recma.js @@ -1,36 +1,9 @@ /** * @typedef {import('estree-jsx').Program} Program - * @typedef {import('hast').Root} Root - */ - -/** - * @typedef {'html' | 'react'} ElementAttributeNameCase - * Specify casing to use for attribute names. - * - * HTML casing is for example `class`, `stroke-linecap`, `xml:lang`. - * React casing is for example `className`, `strokeLinecap`, `xmlLang`. * - * @typedef Options - * Configuration for internal plugin `rehype-recma`. - * @property {ElementAttributeNameCase | null | undefined} [elementAttributeNameCase='react'] - * Specify casing to use for attribute names (default: `'react'`). - * - * This casing is used for hast elements, not for embedded MDX JSX nodes - * (components that someone authored manually). - * @property {StylePropertyNameCase | null | undefined} [stylePropertyNameCase='dom'] - * Specify casing to use for property names in `style` objects (default: `'dom'`). - * - * This casing is used for hast elements, not for embedded MDX JSX nodes - * (components that someone authored manually). - * @property {boolean | null | undefined} [tableCellAlignToStyle=true] - * Turn obsolete `align` props on `td` and `th` into CSS `style` props - * (default: `true`). - * - * @typedef {'css' | 'dom'} StylePropertyNameCase - * Casing to use for property names in `style` objects. + * @typedef {import('hast').Root} Root * - * CSS casing is for example `background-color` and `-webkit-line-clamp`. - * DOM casing is for example `backgroundColor` and `WebkitLineClamp`. + * @typedef {import('../core.js').ProcessorOptions} ProcessorOptions */ import {toEstree} from 'hast-util-to-estree' @@ -39,7 +12,7 @@ import {toEstree} from 'hast-util-to-estree' * A plugin to transform an HTML (hast) tree to a JS (estree). * `hast-util-to-estree` does all the work for us! * - * @param {Readonly | null | undefined} [options] + * @param {Readonly} options * Configuration (optional). * @returns * Transform. diff --git a/packages/mdx/lib/run.js b/packages/mdx/lib/run.js index 90c681035..f3acefed6 100644 --- a/packages/mdx/lib/run.js +++ b/packages/mdx/lib/run.js @@ -1,31 +1,45 @@ +/** + * @typedef {import('mdx/types.js').MDXModule} MDXModule + * + * @typedef {import('./util/resolve-evaluate-options.js').RunOptions} RunOptions + */ + /** @type {new (code: string, ...args: Array) => Function} **/ const AsyncFunction = Object.getPrototypeOf(run).constructor /** - * Asynchronously run code. + * Run code compiled with `outputFormat: 'function-body'`. * - * @param {{toString(): string}} file - * JS document to run. - * @param {unknown} options - * Parameter. - * @return {Promise} - * Anything. + * > ☢️ **Danger**: this `eval`s JavaScript. + * + * @param {{toString(): string}} code + * JavaScript function body to run. + * @param {RunOptions} options + * Configuration (**required**). + * @return {Promise} + * Promise to a module; + * the result is an object with a `default` field set to the component; + * anything else that was exported is available too. */ -export async function run(file, options) { - return new AsyncFunction(String(file))(options) +export async function run(code, options) { + return new AsyncFunction(String(code))(options) } /** - * Synchronously run code. + * Run code, synchronously. + * + * When possible please use the async `run`. + * + * > ☢️ **Danger**: this `eval`s JavaScript. * - * @param {{toString(): string}} file - * JS document to run. - * @param {unknown} options - * Parameter. - * @return {any} - * Anything. + * @param {{toString(): string}} code + * JavaScript function body to run. + * @param {RunOptions} options + * Configuration (**required**). + * @return {MDXModule} + * Module. */ -export function runSync(file, options) { +export function runSync(code, options) { // eslint-disable-next-line no-new-func - return new Function(String(file))(options) + return new Function(String(code))(options) } diff --git a/packages/mdx/lib/util/create-format-aware-processors.js b/packages/mdx/lib/util/create-format-aware-processors.js index da05581cc..e8a831e42 100644 --- a/packages/mdx/lib/util/create-format-aware-processors.js +++ b/packages/mdx/lib/util/create-format-aware-processors.js @@ -7,6 +7,31 @@ * @typedef {import('../compile.js').CompileOptions} CompileOptions */ +/** + * @typedef FormatAwareProcessors + * Result. + * @property {ReadonlyArray} extnames + * Extensions to use. + * @property {Process} process + * Smart processor, async. + * @property {ProcessSync} processSync + * Smart processor, sync. + * + * @callback Process + * Smart processor, async. + * @param {Compatible} vfileCompatible + * MDX or markdown document. + * @return {Promise} + * File. + * + * @callback ProcessSync + * Smart processor, sync. + * @param {Compatible} vfileCompatible + * MDX or markdown document. + * @return {VFile} + * File. + */ + import {createProcessor} from '../core.js' import {md, mdx} from './extnames.js' import {resolveFileAndOptions} from './resolve-file-and-options.js' @@ -16,7 +41,7 @@ import {resolveFileAndOptions} from './resolve-file-and-options.js' * * @param {Readonly | null | undefined} [compileOptions] * Configuration (optional). - * @return {{extnames: ReadonlyArray, process: process, processSync: processSync}} + * @return {FormatAwareProcessors} * Smart processor. */ export function createFormatAwareProcessors(compileOptions) { @@ -42,10 +67,7 @@ export function createFormatAwareProcessors(compileOptions) { /** * Smart processor. * - * @param {Compatible} vfileCompatible - * MDX or markdown document. - * @return {Promise} - * File. + * @type {Process} */ function process(vfileCompatible) { const {file, processor} = split(vfileCompatible) @@ -55,10 +77,7 @@ export function createFormatAwareProcessors(compileOptions) { /** * Sync smart processor. * - * @param {Compatible} vfileCompatible - * MDX or markdown document. - * @return {VFile} - * File. + * @type {ProcessSync} */ function processSync(vfileCompatible) { const {file, processor} = split(vfileCompatible) diff --git a/packages/mdx/lib/util/resolve-evaluate-options.js b/packages/mdx/lib/util/resolve-evaluate-options.js index 707304819..142cdfe77 100644 --- a/packages/mdx/lib/util/resolve-evaluate-options.js +++ b/packages/mdx/lib/util/resolve-evaluate-options.js @@ -1,117 +1,43 @@ /** + * @typedef {import('hast-util-to-jsx-runtime').Fragment} Fragment + * @typedef {import('hast-util-to-jsx-runtime').Jsx} Jsx + * @typedef {import('hast-util-to-jsx-runtime').JsxDev} JsxDev * @typedef {import('mdx/types.js').MDXComponents} Components - * @typedef {import('../core.js').ProcessorOptions} ProcessorOptions + * @typedef {import('../compile.js').CompileOptions} CompileOptions */ /** - * @typedef {JSX.Element | string | null | undefined} Child - * Child. + * @typedef {EvaluateProcessorOptions & RunOptions} EvaluateOptions + * Configuration for `evaluate`. * - * @typedef {EvaluateProcessorOptions & RunnerOptions} EvaluateOptions - * Configuration for evaluation. - * - * @typedef {Omit } EvaluateProcessorOptions + * @typedef {Omit } EvaluateProcessorOptions * Compile configuration without JSX options for evaluation. * - * @typedef {unknown} Fragment - * Represent the children, typically a symbol. - * - * @callback Jsx - * Create a production element. - * @param {unknown} type - * Element type: `Fragment` symbol, tag name (`string`), component. - * @param {Props} props - * Element props, `children`, and maybe `node`. - * @param {string | undefined} [key] - * Dynamicly generated key to use. - * @returns {JSX.Element} - * An element from your framework. - * - * @callback JsxDev - * Create a development element. - * @param {unknown} type - * Element type: `Fragment` symbol, tag name (`string`), component. - * @param {Props} props - * Element props, `children`, and maybe `node`. - * @param {string | undefined} key - * Dynamicly generated key to use. - * @param {boolean} isStaticChildren - * Whether two or more children are passed (in an array), which is whether - * `jsxs` or `jsx` would be used. - * @param {Source} source - * Info about source. - * @param {undefined} self - * Nothing (this is used by frameworks that have components, we don’t). - * @returns {JSX.Element} - * An element from your framework. - * - * @callback MergeComponents - * Custom merge function. - * @param {Components} currentComponents - * Current components from the context. - * @returns {Components} - * Merged components. - * - * @typedef {{children?: Array | Child, node?: Element | undefined, [prop: string]: Array | Child | Element | Value | undefined}} Props - * Properties and children. + * @typedef RunOptions + * Configuration to run compiled code. * - * @typedef RunnerOptions - * Configuration with JSX runtime. + * `Fragment`, `jsx`, and `jsxs` are used when the code is compiled in + * production mode (`development: false`). + * `Fragment` and `jsxDEV` are used when compiled in development mode + * (`development: true`). + * `useMDXComponents` is used when the code is compiled with + * `providerImportSource: '#'` (the exact value of this compile option + * doesn’t matter). * @property {Fragment} Fragment - * Symbol to use for fragments. + * Symbol to use for fragments (**required**). * @property {Jsx | null | undefined} [jsx] * Function to generate an element with static children in production mode. - * @property {Jsx | null | undefined} [jsxs] - * Function to generate an element with dynamic children in production mode. * @property {JsxDev | null | undefined} [jsxDEV] * Function to generate an element in development mode. - * @property {UseMdxComponents | null | undefined} [useMDXComponents] - * Function to get `MDXComponents` from context. - * - * @typedef RuntimeDevelopment - * Runtime fields when development is on. - * @property {Fragment} Fragment - * Fragment. - * @property {Jsx | null | undefined} [jsx] - * Dynamic JSX (optional). - * @property {JsxDev} jsxDEV - * Development JSX. * @property {Jsx | null | undefined} [jsxs] - * Static JSX (optional). - * - * @typedef RuntimeProduction - * Runtime fields when development is off. - * @property {Fragment} Fragment - * Fragment. - * @property {Jsx} jsx - * Dynamic JSX. - * @property {JsxDev | null | undefined} [jsxDEV] - * Development JSX (optional). - * @property {Jsx} jsxs - * Static JSX. - * - * @typedef Source - * Info about source. - * @property {number | undefined} columnNumber - * Column where thing starts (0-indexed). - * @property {string | undefined} fileName - * Name of source file. - * @property {number | undefined} lineNumber - * Line where thing starts (1-indexed). - * - * @typedef {Record} Style - * Style map. + * Function to generate an element with dynamic children in production mode. + * @property {UseMdxComponents | null | undefined} [useMDXComponents] + * Function to get components from context. * * @callback UseMdxComponents - * Get current components from the MDX Context. - * @param {Components | MergeComponents | null | undefined} [components] - * Additional components to use or a function that takes the current - * components and filters/merges/changes them. + * Get components from context. * @returns {Components} * Current components. - * - * @typedef {Style | boolean | number | string} Value - * Primitive property value and `Style` map. */ // Fix to show references to above types in VS Code. @@ -122,7 +48,7 @@ * * @param {Readonly | null | undefined} options * Configuration. - * @returns {{compiletime: ProcessorOptions, runtime: RunnerOptions}} + * @returns {{compiletime: CompileOptions, runtime: RunOptions}} * Split options. */ export function resolveEvaluateOptions(options) { diff --git a/packages/mdx/package.json b/packages/mdx/package.json index 6ac5b9332..dd8359792 100644 --- a/packages/mdx/package.json +++ b/packages/mdx/package.json @@ -54,6 +54,7 @@ "estree-util-to-js": "^2.0.0", "estree-walker": "^3.0.0", "hast-util-to-estree": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "periscopic": "^3.0.0", "remark-mdx": "^2.0.0", diff --git a/packages/mdx/readme.md b/packages/mdx/readme.md index a8da18481..8c645d4a2 100644 --- a/packages/mdx/readme.md +++ b/packages/mdx/readme.md @@ -19,13 +19,6 @@ MDX compiler. * [Install](#install) * [Use](#use) * [API](#api) - * [`compile(file, options?)`](#compilefile-options) - * [`compileSync(file, options?)`](#compilesyncfile-options) - * [`evaluate(file, options)`](#evaluatefile-options) - * [`evaluateSync(file, options)`](#evaluatesyncfile-options) - * [`run(functionBody, options)`](#runfunctionbody-options) - * [`runSync(functionBody, options)`](#runsyncfunctionbody-options) - * [`createProcessor(options)`](#createprocessoroptions) * [Types](#types) * [Architecture](#architecture) * [Security](#security) @@ -39,27 +32,33 @@ It can also evaluate MDX code. ## When should I use this? -This is the core compiler for turning MDX into JavaScript and which gives you -the most control. -If you’re using a bundler (webpack, Rollup, esbuild), or a site builder (Gatsby, -Next.js) or build system (Vite, WMR) which comes with a bundler, you’re better -off using an integration: see [§ Integrations][integrations]. +This is the core compiler for turning MDX into JavaScript which gives you the +most control. +If you’re using a bundler (Rollup, esbuild, webpack), a site builder (Next.js), +or build system (Vite) which comes with a bundler, you’re better off using an +integration: see [§ Integrations][integrations]. ## Install -This package is [ESM only][esm]: -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install @mdx-js/mdx ``` -[yarn][]: +In Deno with [`esm.sh`][esmsh]: -```sh -yarn add @mdx-js/mdx +```tsx +import {compile} from 'https://esm.sh/@mdx-js/mdx@2' +``` + +In browsers with [`esm.sh`][esmsh]: + +```html + ``` ## Use @@ -74,7 +73,7 @@ export function Thing() { # Hello, ``` -Add some code in `example.js` to compile `example.mdx` to JavaScript: +…and some code in `example.js` to compile `example.mdx` to JavaScript: ```tsx import fs from 'node:fs/promises' @@ -92,26 +91,18 @@ Yields roughly: import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' export function Thing() { - return _jsx(_Fragment, {children: 'World'}) + return _jsx(_Fragment, {children: 'World!'}) } function _createMdxContent(props) { - const _components = { - h1: 'h1', - ...props.components - } - return _jsxs(_components.h1, { - children: ['Hello ', _jsx(Thing, {})] - }) + const _components = {h1: 'h1', ...props.components} + return _jsxs(_components.h1, {children: ['Hello, ', _jsx(Thing, {})]}) } export default function MDXContent(props = {}) { const {wrapper: MDXLayout} = props.components || {} return MDXLayout - ? _jsx(MDXLayout, { - ...props, - children: _jsx(_createMdxContent, props) - }) + ? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {...props})}) : _createMdxContent(props) } ``` @@ -121,30 +112,40 @@ See [§ Using MDX][using-mdx] for more on how MDX work and how to use the result ## API This package exports the following identifiers: -[`compile`][compile], -[`compileSync`][compile-sync], -[`evaluate`][eval], -[`evaluateSync`](#evaluatesyncfile-options), -[`run`][run], -[`runSync`](#runsyncfunctionbody-options), and -[`createProcessor`][create-processor]. +[`compile`][api-compile], +[`compileSync`][api-compile-sync], +[`createProcessor`][api-create-processor], +[`evaluate`][api-evaluate], +[`evaluateSync`][api-evaluate-sync], +[`nodeTypes`][api-node-types], +[`run`][api-run], and +[`runSync`][api-run-sync]. There is no default export. ### `compile(file, options?)` Compile MDX to JS. -###### `file` +###### Parameters + +* `file` ([`Compatible` from `vfile`][vfile-compatible]) + — MDX document to parse +* `options` ([`CompileOptions`][api-compile-options], optional) + — compile configuration + +###### Returns + +Promise to compiled file ([`Promise`][vfile]). -MDX document to parse (`string`, [`Buffer`][buffer] in UTF-8, [`vfile`][vfile], -or anything that can be given to `vfile`). +###### Examples -
-Expand example +The input value for `file` can be many different things. +You can pass a `string`, `Uint8Array` in UTF-8, [`VFile`][vfile], or anything +that can be given to `new VFile`. ```tsx -import {VFile} from 'vfile' import {compile} from '@mdx-js/mdx' +import {VFile} from 'vfile' await compile(':)') await compile(Buffer.from(':-)')) @@ -152,840 +153,927 @@ await compile({path: 'path/to/file.mdx', value: '🥳'}) await compile(new VFile({path: 'path/to/file.mdx', value: '🤭'})) ``` -
- -###### `options.remarkPlugins` - -List of [remark plugins][remark-plugins], presets, and pairs. - -
-Expand example - -```tsx -import remarkFrontmatter from 'remark-frontmatter' // YAML and such. -import remarkGfm from 'remark-gfm' // Tables, footnotes, strikethrough, task lists, literal URLs. - -await compile(file, {remarkPlugins: [remarkGfm]}) // One plugin. -await compile(file, {remarkPlugins: [[remarkFrontmatter, 'toml']]}) // A plugin with options. -await compile(file, {remarkPlugins: [remarkGfm, remarkFrontmatter]}) // Two plugins. -await compile(file, {remarkPlugins: [[remarkGfm, {singleTilde: false}], remarkFrontmatter]}) // Two plugins, first w/ options. -``` - -
- -###### `options.rehypePlugins` - -List of [rehype plugins][rehype-plugins], presets, and pairs. - -
-Expand example +The output `VFile` can be used to access more than the generated code: ```tsx -import rehypeKatex from 'rehype-katex' // Render math with KaTeX. -import remarkMath from 'remark-math' // Support math like `$so$`. +import {compile} from '@mdx-js/mdx' +import remarkPresetLintConsistent from 'remark-preset-lint-consistent' // Lint rules to check for consistent markdown. +import {reporter} from 'vfile-reporter' -await compile(file, {rehypePlugins: [rehypeKatex], remarkPlugins: [remarkMath]}) +const file = await compile('*like this* or _like this_?', {remarkPlugins: [remarkPresetLintConsistent]}) -await compile(file, { - // A plugin with options: - rehypePlugins: [[rehypeKatex, {strict: true, throwOnError: true}]], - remarkPlugins: [remarkMath] -}) +console.error(reporter(file)) ``` -
- -###### `options.recmaPlugins` - -List of recma plugins. -This is a new ecosystem, currently in beta, to transform [esast][] trees -(JavaScript). +Yields: -###### `options.remarkRehypeOptions` +```txt + 1:16-1:27 warning Emphasis should use `*` as a marker emphasis-marker remark-lint -Options to pass through to [`remark-rehype`][remark-rehype]. -The option `allowDangerousHtml` will always be set to `true` and the MDX nodes -are passed through. -In particular, you might want to pass `clobberPrefix`, `footnoteLabel`, and -`footnoteBackLabel`. +⚠ 1 warning +``` -
-Expand example +### `compileSync(file, options?)` -```tsx -compile({value: '…'}, {remarkRehypeOptions: {clobberPrefix: 'comment-1'}}) -``` +Synchronously compile MDX to JS. -
+When possible please use the async [`compile`][api-compile]. -###### `options.mdExtensions` +###### Parameters -List of markdown extensions, with dot (`Array`, default: `['.md', -'.markdown', '.mdown', '.mkdn', '.mkd', '.mdwn', '.mkdown', '.ron']`). +* `file` ([`Compatible` from `vfile`][vfile-compatible]) + — MDX document to parse +* `options` ([`CompileOptions`][api-compile-options], optional) + — compile configuration -###### `options.mdxExtensions` +###### Returns -List of MDX extensions, with dot (`Array`, default: `['.mdx']`). -Has no effect in `compile` or `evaluate` but does affect -[§ Integrations][integrations]. +Compiled file ([`VFile`][vfile]). -###### `options.format` +### `createProcessor(options?)` -Format the file is in (`'detect' | 'mdx' | 'md'`, default: `'detect'`). +Create a processor to compile markdown or MDX to JavaScript. -* `'detect'` — use `'markdown'` for files with an extension in `mdExtensions` - and `'mdx'` otherwise -* `'mdx'` — treat file as [MDX][mdx-syntax] -* `'md'` — treat file as plain vanilla markdown +> **Note**: `format: 'detect'` is not allowed in `ProcessorOptions`. -The format cannot be detected if a file is passed without a path or extension: -`mdx` will be assumed. -So pass a full vfile (with `path`) or an object with a path. +###### Parameters -
-Expand example +* `options` ([`ProcessorOptions`][api-processor-options], optional) + — process configuration -```tsx -compile({value: '…'}) // Seen as MDX -compile({value: '…'}, {format: 'md'}) // Seen as markdown -compile({path: 'readme.md', value: '…'}) // Seen as markdown +###### Returns -// Please do not use `.md` for MDX as other tools won’t know how to handle it. -compile({path: 'readme.md', value: '…'}, {format: 'mdx'}) // Seen as MDX -compile({path: 'readme.md', value: '…'}, {mdExtensions: []}) // Seen as MDX -``` +Processor ([`Processor` from `unified`][unified-processor]). -
+### `evaluate(file, options)` -This option mostly affects [§ Integrations][integrations] -because in those it affects *which* files are “registered”: +[Compile][] and [run][] MDX. -* `format: 'mdx'` registers the extensions in `options.mdxExtensions` -* `format: 'md'` registers the extensions in `options.mdExtensions` -* `format: 'detect'` registers both lists of extensions +When you trust your content, `evaluate` can work. +When possible, use [`compile`][api-compile], write to a file, and then run with +Node or use one of the [§ Integrations][integrations]. -###### `options.outputFormat` +> ☢️ **Danger**: it’s called **evaluate** because it `eval`s JavaScript. -Output format to generate (`'function-body' | 'program'`, default: `'program'`). -In most cases `'program'` should be used, as it results in a whole program. -Internally, [`evaluate`][eval] uses `outputFormat: 'function-body'` to compile -to code that can be `eval`ed with [`run`][run]. -In some cases, you might want to do what `evaluate` does in separate steps -yourself, such as when compiling on the server and running on the client. +###### Parameters -The `'program'` format will use import statements to import the runtime (and -optionally provider) and use an export statement to yield the `MDXContent` -component. +* `file` ([`Compatible` from `vfile`][vfile-compatible]) + — MDX document to parse +* `options` ([`EvaluateOptions`][api-evaluate-options], **required**) + — configuration -The `'function-body'` format will get the runtime (and optionally provider) from -`arguments[0]`, rewrite export statements, and use a return statement to yield -what was exported. -Normally, this output format will throw on `import` (and `export … from`) -statements, but you can support them by setting -[`options.useDynamicImport`][usedynamicimport]. +###### Returns -
-Expand example +Promise to a module ([`Promise` from `mdx/types.js`][mdx-types-module]). -A module `example.js`: +The result is an object with a `default` field set to the component; +anything else that was exported is available too. +For example, assuming the contents of `example.mdx` from [§ Use][use] was in +`file`, then: ```tsx -import {compile} from '@mdx-js/mdx' - -const code = 'export const no = 3.14\n\n# hi {no}' +import {evaluate} from '@mdx-js/mdx' +import * as runtime from 'react/jsx-runtime' -console.log(String(await compile(code, {outputFormat: 'program'}))) // Default -console.log(String(await compile(code, {outputFormat: 'function-body'}))) +console.log(await evaluate(file, runtime)) ``` …yields: ```tsx -/* @jsxRuntime automatic @jsxImportSource react */ -import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' -export const no = 3.14 -function _createMdxContent(props) { /* … */ } -export default function MDXContent(props = {}) { /* … */ } -``` - -```tsx -const {Fragment: _Fragment, jsx: _jsx} = arguments[0] -const no = 3.14 -function _createMdxContent(props) { /* … */ } -function MDXContent(props = {}) { /* … */ } -return {no, default: MDXContent} +{Thing: [Function: Thing], default: [Function: MDXContent]} ``` -
- -###### `options.useDynamicImport` - -Whether to compile to dynamic import expressions (`boolean`, default: `false`). -This option applies when [`options.outputFormat`][outputformat] is -`'function-body'`. +###### Notes -`@mdx-js/mdx` can turn import statements (`import x from 'y'`) into dynamic -imports (`const {x} = await import('y')`). -This is useful because import statements only work at the top level of -JavaScript modules, whereas `import()` is available inside function bodies. +Compiling (and running) MDX takes time. -When you turn `useDynamicImport` on, you should probably set [`options.baseUrl`][baseurl] too. +If you are live-rendering a string of MDX that often changes using a virtual DOM +based framework (such as React), one performance improvement is to call the +`MDXContent` component yourself. +The reason is that the `evaluate` creates a new function each time, which cannot +be diffed: -
-Expand example +```diff + const {default: MDXContent} = await evaluate('…') -Say we have a couple modules: +- ++MDXContent(props) +``` -```tsx -// meta.js: -export const title = 'World' +### `evaluateSync(file, options)` -// numbers.js: -export const no = 3.14 +Compile and run MDX, synchronously. -// example.js: -import {compileSync} from '@mdx-js/mdx' +When possible please use the async [`evaluate`][api-evaluate]. -const code = `import {name} from './meta.js' -export {no} from './numbers.js' +> ☢️ **Danger**: it’s called **evaluate** because it `eval`s JavaScript. -# hi {name}!` +###### Parameters -console.log(String(compileSync(code, {outputFormat: 'function-body', useDynamicImport: true}))) -``` +* `file` ([`Compatible` from `vfile`][vfile-compatible]) + — MDX document to parse +* `options` ([`EvaluateOptions`][api-evaluate-options], **required**) + — configuration -…now running `node example.js` yields: +###### Returns -```tsx -const {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0] -const {name} = await import('./meta.js') -const {no} = await import('./numbers.js') -function _createMdxContent(props) { /* … */ } -function MDXContent(props = {}) { /* … */ } -return {no, default: MDXContent} -``` +Module ([`MDXModule` from `mdx/types.js`][mdx-types-module]). -
+### `nodeTypes` -###### `options.baseUrl` +List of node types made by `mdast-util-mdx`, which have to be passed +through untouched from the mdast tree to the hast tree (`Array`). -Resolve `import`s (and `export … from`, and `import.meta.url`) from this URL -(`string?`, example: `import.meta.url`). +### `run(code, options)` -Relative specifiers are non-absolute URLs that start with `/`, `./`, or `../`. -For example: `/index.js`, `./folder/file.js`, or `../main.js`. +Run code compiled with `outputFormat: 'function-body'`. -This option is useful when code will run in a different place. -One example is when `.mdx` files are in path *a* but compiled to path *b* and -imports should run relative the path *b*. -Another example is when evaluating code, whether in Node or a browser. +> ☢️ **Danger**: this `eval`s JavaScript. -
-Expand example +###### Parameters -Say we have a module `example.js`: +* `code` ([`VFile`][vfile] or `string`) + — JavaScript function body to run +* `options` ([`RunOptions`][api-run-options], **required**) + — configuration -```tsx -import {compile} from '@mdx-js/mdx' +###### Returns -const code = 'export {number} from "./data.js"\n\n# hi' -const baseUrl = 'https://a.full/url' // Typically `import.meta.url` +Promise to a module ([`Promise` from `mdx/types.js`][mdx-types-module]); +the result is an object with a `default` field set to the component; +anything else that was exported is available too. -console.log(String(await compile(code, {baseUrl}))) -``` +###### Example -…now running `node example.js` yields: +On the server: ```tsx -import {Fragment as _Fragment, jsx as _jsx} from 'react/jsx-runtime' -export {number} from 'https://a.full/data.js' -function _createMdxContent(props) { /* … */ } -function MDXContent(props = {}) { /* … */ } -export default MDXContent -``` - -
- -###### `options.development` - -Whether to add extra info to error messages in generated code -(`boolean?`, default: `false`). -This also results in the development automatic JSX runtime (`/jsx-dev-runtime`, -`jsxDEV`) being used, which passes positional info to nodes. -The default can be set to `true` in Node.js through environment variables: set -`NODE_ENV=development`. - -
-Expand example - -Say we had some MDX that references a component that can be passed or provided -at runtime: +import {compile} from '@mdx-js/mdx' -```mdx -**Note**: some stuff. +const code = String(await compile('# hi', {outputFormat: 'function-body'})) +// To do: send `code` to the client somehow. ``` -And a module to evaluate that: +On the client: ```tsx -import fs from 'node:fs/promises' +import {run} from '@mdx-js/mdx' import * as runtime from 'react/jsx-runtime' -import {evaluate} from '@mdx-js/mdx' - -const path = 'example.mdx' -const value = await fs.readFile(path) -const MDXContent = (await evaluate({path, value}, runtime)).default - -console.log(MDXContent()) -``` - -Running that would normally (production) yield: - -```txt -Error: Expected component `NoteIcon` to be defined: you likely forgot to import, pass, or provide it. - at _missingMdxReference (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :27:9) - at _createMdxContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :15:20) - at MDXContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :9:9) - at main (…/example.js:11:15) -``` - -But if we change add `development: true` to our example: - -```diff -@@ -7,6 +7,6 @@ - import fs from 'node:fs/promises' --import * as runtime from 'react/jsx-runtime' -+import * as runtime from 'react/jsx-dev-runtime' - import {evaluate} from '@mdx-js/mdx' - - const path = 'example.mdx' - const value = await fs.readFile(path) --const MDXContent = (await evaluate({path, value}, runtime)).default -+const MDXContent = (await evaluate({path, value}, {development: true, ...runtime})).default - console.log(MDXContent({})) -``` - -And we’d run it again, we’d get: - -```txt -Error: Expected component `NoteIcon` to be defined: you likely forgot to import, pass, or provide it. -It’s referenced in your code at `1:9-1:21` in `example.mdx` -provide it. - at _missingMdxReference (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :27:9) - at _createMdxContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :15:20) - at MDXContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :9:9) - at main (…/example.js:11:15) -``` - -
-###### `options.SourceMapGenerator` - -The `SourceMapGenerator` class from [`source-map`][source-map] (optional). -When given, the resulting file will have a `map` field set to a source map (in -object form). - -
-Expand example - -Assuming `example.mdx` from [§ Use][use] exists, then: - -```tsx -import fs from 'node:fs/promises' -import {SourceMapGenerator} from 'source-map' -import {compile} from '@mdx-js/mdx' +const code = '' // To do: get `code` from server somehow. -const file = await compile( - {path: 'example.mdx', value: await fs.readFile('example.mdx')}, - {SourceMapGenerator} -) +const {default: Content} = await run(code, runtime) -console.log(file.map) +console.log(Content) ``` …yields: ```tsx -{ - file: 'example.mdx', - mappings: ';;aAAaA,QAAQ;YAAQ;;;;;;;;iBAE3B', - names: ['Thing'], - sources: ['example.mdx'], - version: 3 -} -``` - -
- -###### `options.providerImportSource` - -Place to import a provider from (`string?`, example: `'@mdx-js/react'`). -Useful for runtimes that support context (React, Preact). -The provider must export a `useMDXComponents`, which is called to access an -object of components. - -
-Expand example - -If `file` is the contents of `example.mdx` from [§ Use][use], then: - -```tsx -compile(file, {providerImportSource: '@mdx-js/react'}) +[Function: MDXContent] ``` -…yields this difference: +### `runSync(code, options)` -```diff - /* @jsxRuntime automatic @jsxImportSource react */ - import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' -+import {useMDXComponents as _provideComponents} from '@mdx-js/react' - - export function Thing() { - return _jsx(_Fragment, {children: 'World'}) - } - - function _createMdxContent(props) { - const _components = { - h1: 'h1', -+ ..._provideComponents(), - ...props.components - } - return _jsxs(_components.h1, {children: ['Hello ', _jsx(Thing, {})]}) - } - - export default function MDXContent(props = {}) { -- const {wrapper: MDXLayout} = props.components || {} -+ const {wrapper: MDXLayout} = { -+ ..._provideComponents(), -+ ...props.components -+ } - - return MDXLayout - ? _jsx(MDXLayout, { - ...props, - children: _jsx(_createMdxContent, {}) - }) - : _createMdxContent() -``` +Run code, synchronously. -
+When possible please use the async [`run`][api-run]. -###### `options.jsx` +> ☢️ **Danger**: this `eval`s JavaScript. -Whether to keep JSX (`boolean?`, default: `false`). -The default is to compile JSX away so that the resulting file is immediately -runnable. +###### Parameters -
-Expand example +* `code` ([`VFile`][vfile] or `string`) + — JavaScript function body to run +* `options` ([`RunOptions`][api-run-options], **required**) + — configuration -If `file` is the contents of `example.mdx` from [§ Use][use], then: - -```tsx -compile(file, {jsx: true}) -``` - -…yields this difference: - -```diff - /* @jsxRuntime automatic @jsxImportSource react */ --import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' - - export function Thing() { -- return _jsx(_Fragment, {children: 'World'}) -+ return <>World! - } - - function _createMdxContent(props) { - const _components = { - h1: 'h1', - ...props.components - } -- return _jsxs(_components.h1, {children: ['Hello ', _jsx(Thing, {})]}) -+ return <_components.h1>{"Hello "} - } - - export default function MDXContent(props = {}) { - const {wrapper: MDXLayout} = props.components || {} - return MDXLayout -- ? _jsx(MDXLayout, { -- ...props, -- children: _jsx(_createMdxContent, props) -- }) -+ ? <_createMdxContent {...props} /> - : _createMdxContent(props) - } - } -``` +###### Returns -
+Module ([`MDXModule` from `mdx/types.js`][mdx-types-module]). -###### `options.jsxRuntime` +### `CompileOptions` -JSX runtime to use (`'automatic' | 'classic'`, default: `'automatic'`). -The classic runtime compiles to calls such as `h('p')`, the automatic runtime -compiles to `import _jsx from '$importSource/jsx-runtime'\n_jsx('p')`. +Configuration for `compile` (TypeScript type). -
-Expand example +`CompileOptions` is the same as [`ProcessorOptions`][api-processor-options] +with the exception that the `format` option supports a `'detect'` value, +which is the default. +The `'detect'` format means to use `'md'` for files with an extension in +`mdExtensions` and `'mdx'` otherwise. -If `file` is the contents of `example.mdx` from [§ Use][use], then: +###### Type ```tsx -compile(file, {jsxRuntime: 'classic'}) -``` - -…yields this difference: - -```diff --/* @jsxRuntime automatic @jsxImportSource react */ --import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' -+/* @jsxRuntime classic @jsx React.createElement @jsxFrag React.Fragment */ -+import React from 'react' - - export function Thing() { -- return _jsx(_Fragment, {children: 'World'}) -+ return React.createElement(React.Fragment, null, 'World!') - } -… +/** + * Configuration for `compile` + */ +type CompileOptions = Omit & { + /** + * Format of `file` (default: `'detect'`). + */ + format?: 'detect' | 'md' | 'mdx' | null | undefined +} ``` -
+### `EvaluateOptions` -###### `options.jsxImportSource` +Configuration for `evaluate` (TypeScript type). -Place to import automatic JSX runtimes from (`string?`, default: `'react'`). -When in the `automatic` runtime, this is used to define an import for -`Fragment`, `jsx`, `jsxs`, and `jsxDEV`. +`EvaluateOptions` is the same as [`CompileOptions`][api-compile-options], +except that the options `jsx`, `jsxImportSource`, `jsxRuntime`, `outputFormat`, +`pragma`, `pragmaFrag`, `pragmaImportSource`, and `providerImportSource` are +not allowed, and that [`RunOptions`][api-run-options] are also used. -
-Expand example - -If `file` is the contents of `example.mdx` from [§ Use][use], then: +###### Type ```tsx -compile(file, {jsxImportSource: 'preact'}) -``` - -…yields this difference: - -```diff --/* @jsxRuntime automatic @jsxImportSource react */ --import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' -+/* @jsxRuntime automatic @jsxImportSource preact */ -+import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from 'preact/jsx-runtime' +/** + * Configuration for `evalutate`. + */ +type EvaluateOptions = Omit< + CompileOptions, + | 'jsx' + | 'jsxImportSource' + | 'jsxRuntime' + | 'outputFormat' + | 'pragma' + | 'pragmaFrag' + | 'pragmaImportSource' + | 'providerImportSource' +> & + RunOptions ``` -
- -###### `options.pragma` - -Pragma for JSX (`string?`, default: `'React.createElement'`). -When in the `classic` runtime, this is used as an identifier for function calls: -`` to `React.createElement('x')`. +### `Fragment` -You should most probably define `pragmaFrag` and `pragmaImportSource` too when -changing this. - -
-Expand example - -If `file` is the contents of `example.mdx` from [§ Use][use], then: - -```tsx -compile(file, { - jsxRuntime: 'classic', - pragma: 'preact.createElement', - pragmaFrag: 'preact.Fragment', - pragmaImportSource: 'preact/compat' -}) -``` +Represent the children, typically a symbol (TypeScript type). -…yields this difference: +###### Type -```diff --/* @jsxRuntime classic @jsx React.createElement @jsxFrag React.Fragment */ --import React from 'react' -+/* @jsxRuntime classic @jsx preact.createElement @jsxFrag preact.Fragment */ -+import preact from 'preact/compat' - - export function Thing() { -- return React.createElement(React.Fragment, null, 'World!') -+ return preact.createElement(preact.Fragment, null, 'World!') - } -… +```ts +type Fragment = unknown ``` -
- -###### `options.pragmaFrag` - -Pragma for JSX fragments (`string?`, default: `'React.Fragment'`). -When in the `classic` runtime, this is used as an identifier for fragments: `<>` -to `React.createElement(React.Fragment)`. - -See `options.pragma` for an example. - -###### `options.pragmaImportSource` - -Where to import the identifier of `pragma` from (`string?`, default: `'react'`). -When in the `classic` runtime, this is used to import the `pragma` function. -To illustrate with an example: when `pragma` is `'a.b'` and `pragmaImportSource` -is `'c'` this following will be generated: `import a from 'c'`. - -See `options.pragma` for an example. - -###### `options.elementAttributeNameCase` +### `Jsx` -Specify casing to use for attribute names (`'html' | 'react`, default: -`'react'`). +Create a production element (TypeScript type). -This casing is used for hast elements, not for embedded MDX JSX nodes -(components that someone authored manually). +###### Parameters -###### `options.stylePropertyNameCase` - -Specify casing to use for property names in `style` objects (`'css' | 'dom`, -default: `'dom'`). - -This casing is used for hast elements, not for embedded MDX JSX nodes -(components that someone authored manually). - -###### `options.tableCellAlignToStyle` - -Turn obsolete `align` props on `td` and `th` into CSS `style` props (default: -`true`). +* `type` (`unknown`) + — element type: `Fragment` symbol, tag name (`string`), component +* `props` (`Props`) + — element props and `children` +* `key` (`string` or `undefined`) + — key to use ###### Returns -`Promise` — Promise that resolves to the compiled JS as a [vfile][]. - -
-Expand example - -```tsx -import remarkPresetLintConsistent from 'remark-preset-lint-consistent' // Lint rules to check for consistent markdown. -import {reporter} from 'vfile-reporter' -import {compile} from '@mdx-js/mdx' - -const file = await compile('*like this* or _like this_?', {remarkPlugins: [remarkPresetLintConsistent]}) - -console.error(reporter(file)) -``` - -Yields: - -```txt - 1:16-1:27 warning Emphasis should use `*` as a marker emphasis-marker remark-lint - -⚠ 1 warning -``` - -
- -### `compileSync(file, options?)` - -Compile MDX to JS. -Synchronous version of `compile`. -When possible please use the async `compile`. - -### `evaluate(file, options)` - -> ☢️ **Danger**: It’s called **evaluate** because it `eval`s JavaScript. - -[Compile][] and [run][] MDX. -When possible, please use `compile`, write to a file, and then run with Node, -or use one of the -[§ Integrations][integrations]. -But if you trust your content, `evaluate` can work. - -Typically, `import` (or `export … from`) do not work here. -They can be compiled to dynamic `import()` by passing -[`options.useDynamicImport`][usedynamicimport]. - -###### `file` - -See [`compile`][compile]. +Element from your framework (`JSX.Element`). -###### `options` +### `JsxDev` -Most options are the same as [`compile`][compile], with the following -exceptions: +Create a development element (TypeScript type). -* `providerImportSource` is replaced by `useMDXComponents` -* `jsx*` and `pragma*` options are replaced by `Fragment`, `jsx`, `jsxs`, and - `jsxDEV` -* `outputFormat` is set to `function-body` +###### Parameters -###### `options.jsx` +* `type` (`unknown`) + — element type: `Fragment` symbol, tag name (`string`), component +* `props` (`Props`) + — element props and `children` +* `key` (`string` or `undefined`) + — key to use +* `isStaticChildren` (`boolean`) + — whether two or more children are passed (in an array), which is whether + `jsxs` or `jsx` would be used +* `source` (`Source`) + — info about source +* `self` (`unknown`) + — context object (`this`) -###### `options.jsxs` +### `ProcessorOptions` -###### `options.jsxDEV` +Configuration for `createProcessor` (TypeScript type). -###### `options.Fragment` +###### Fields -These options are required: `Fragment` always, when `development: true` -then `jsxDEV`, when `development: false` then `jsx` and `jsxs`. -They come from an automatic JSX runtime that you must import yourself. +* `SourceMapGenerator` (`SourceMapGenerator` from [`source-map`][source-map], + optional) + — add a source map (object form) as the `map` field on the resulting file -
-Expand example +
Expand example -```tsx -import * as runtime from 'react/jsx-runtime' - -const {default: Content} = await evaluate('# hi', {...otherOptions, ...runtime}) -``` - -
+ Assuming `example.mdx` from [§ Use][use] exists, then: -###### `options.useMDXComponents` + ```tsx + import fs from 'node:fs/promises' + import {compile} from '@mdx-js/mdx' + import {SourceMapGenerator} from 'source-map' -Needed if you want to support a provider. - -
-Expand example - -```tsx -import * as provider from '@mdx-js/react' -import * as runtime from 'react/jsx-runtime' + const file = await compile( + {path: 'example.mdx', value: await fs.readFile('example.mdx')}, + {SourceMapGenerator} + ) -const {default: Content} = await evaluate('# hi', { - ...otherOptions, - ...provider, - ...runtime -}) -``` + console.log(file.map) + ``` -
+ …yields: -###### Returns + ```tsx + { + file: 'example.mdx', + mappings: ';;aAAaA,QAAQ;YAAQ;;;;;;;;iBAE3B', + names: ['Thing'], + sources: ['example.mdx'], + version: 3 + } + ``` -`Promise` — Promise that resolves to something that looks a bit like -a module: an object with a `default` field set to the component and anything -else that was exported from the MDX file available too. +
+* `baseUrl` (`string`, optional, example: `import.meta.url`) + — resolve `import`s (and `export … from`, and `import.meta.url`) from this + URL; + this option is useful when code will run in a different place, such as when + `.mdx` files are in path *a* but compiled to path *b* and imports should + run relative the path *b*, or when evaluating code, whether in Node or a + browser + +
Expand example + + Say we have a module `example.js`: + + ```tsx + import {compile} from '@mdx-js/mdx' + + const code = 'export {number} from "./data.js"\n\n# hi' + const baseUrl = 'https://a.full/url' // Typically `import.meta.url` + + console.log(String(await compile(code, {baseUrl}))) + ``` + + …now running `node example.js` yields: + + ```tsx + import {Fragment as _Fragment, jsx as _jsx} from 'react/jsx-runtime' + export {number} from 'https://a.full/data.js' + function _createMdxContent(props) { /* … */ } + function MDXContent(props = {}) { /* … */ } + export default MDXContent + ``` + +
+* `development` (`boolean`, default: `false`) + — whether to add extra info to error messages in generated code and use the + development automatic JSX runtime (`Fragment` and `jsxDEV` from + `/jsx-dev-runtime`); + when using the webpack loader (`@mdx-js/loader`) or the Rollup integration + (`@mdx-js/rollup`) through Vite, this is automatically inferred from how + you configure those tools + +
Expand example + + Say we had some MDX that references a component that can be passed or + provided at runtime: + + ```mdx + **Note**: some stuff. + ``` + + And a module to evaluate that: + + ```tsx + import fs from 'node:fs/promises' + import {evaluate} from '@mdx-js/mdx' + import * as runtime from 'react/jsx-runtime' + + const path = 'example.mdx' + const value = await fs.readFile(path) + const MDXContent = (await evaluate({path, value}, runtime)).default + + console.log(MDXContent({})) + ``` + + …running that would normally (production) yield: + + ```txt + Error: Expected component `NoteIcon` to be defined: you likely forgot to import, pass, or provide it. + at _missingMdxReference (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :27:9) + at _createMdxContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :15:20) + at MDXContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :9:9) + at main (…/example.js:11:15) + ``` + + …but if we add `development: true` to our example: + + ```diff + @@ -7,6 +7,6 @@ + import fs from 'node:fs/promises' + -import * as runtime from 'react/jsx-runtime' + +import * as runtime from 'react/jsx-dev-runtime' + import {evaluate} from '@mdx-js/mdx' + + const path = 'example.mdx' + const value = await fs.readFile(path) + -const MDXContent = (await evaluate({path, value}, runtime)).default + +const MDXContent = (await evaluate({path, value}, {development: true, ...runtime})).default + + console.log(MDXContent({})) + ``` + + …and we’d run it again, we’d get: + + ```txt + Error: Expected component `NoteIcon` to be defined: you likely forgot to import, pass, or provide it. + It’s referenced in your code at `1:9-1:21` in `example.mdx` + provide it. + at _missingMdxReference (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :27:9) + at _createMdxContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :15:20) + at MDXContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), :9:9) + at main (…/example.js:11:15) + ``` + +
+* `elementAttributeNameCase` (`'html'` or `'react`, default: `'react'`) + — casing to use for attribute names; + HTML casing is for example `class`, `stroke-linecap`, `xml:lang`; + React casing is for example `className`, `strokeLinecap`, `xmlLang`; + for JSX components written in MDX, the author has to be aware of which + framework they use and write code accordingly; + for AST nodes generated by this project, this option configures it +* `format` (`'md'` or `'mdx'`, default: `'mdx'`) + — format of the file; + `'md'` means treat as markdown and `'mdx'` means treat as [MDX][mdx-syntax] + +
Expand example + + ```tsx + compile('…') // Seen as MDX. + compile('…', {format: 'mdx'}) // Seen as MDX. + compile('…', {format: 'md'}) // Seen as markdown. + ``` + +
+* `jsx` (`boolean`, default: `false`) + — whether to keep JSX; + the default is to compile JSX away so that the resulting file is + immediately runnable. + +
Expand example + + If `file` is the contents of `example.mdx` from [§ Use][use], then: + + ```tsx + compile(file, {jsx: true}) + ``` + + …yields this difference: + + ```diff + /* @jsxRuntime automatic @jsxImportSource react */ + -import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' + + export function Thing() { + - return _jsx(_Fragment, {children: 'World'}) + + return <>World! + } + + function _createMdxContent(props) { + const _components = { + h1: 'h1', + ...props.components + } + - return _jsxs(_components.h1, {children: ['Hello ', _jsx(Thing, {})]}) + + return <_components.h1>{"Hello "} + } + + export default function MDXContent(props = {}) { + const {wrapper: MDXLayout} = props.components || {} + return MDXLayout + - ? _jsx(MDXLayout, { + - ...props, + - children: _jsx(_createMdxContent, props) + - }) + + ? <_createMdxContent {...props} /> + : _createMdxContent(props) + } + } + ``` + +
+* `jsxImportSource` (`string`, default: `'react'`) + — place to import automatic JSX runtimes from; + when in the `automatic` runtime, this is used to define an import for + `Fragment`, `jsx`, `jsxDEV`, and `jsxs` + +
Expand example + + If `file` is the contents of `example.mdx` from [§ Use][use], then: + + ```tsx + compile(file, {jsxImportSource: 'preact'}) + ``` + + …yields this difference: + + ```diff + -/* @jsxRuntime automatic @jsxImportSource react */ + -import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' + +/* @jsxRuntime automatic @jsxImportSource preact */ + +import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from 'preact/jsx-runtime' + ``` + +
+* `jsxRuntime` (`'automatic'` or `'classic'`, default: `'automatic'`) + — JSX runtime to use; + the automatic runtime compiles to `import _jsx from + '$importSource/jsx-runtime'\n_jsx('p')`; + the classic runtime compiles to calls such as `h('p')` + +
Expand example + + If `file` is the contents of `example.mdx` from [§ Use][use], then: + + ```tsx + compile(file, {jsxRuntime: 'classic'}) + ``` + + …yields this difference: + + ```diff + -/* @jsxRuntime automatic @jsxImportSource react */ + -import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' + +/* @jsxRuntime classic @jsx React.createElement @jsxFrag React.Fragment */ + +import React from 'react' + + export function Thing() { + - return _jsx(_Fragment, {children: 'World'}) + + return React.createElement(React.Fragment, null, 'World!') + } + … + ``` + +
+* `outputFormat` (`'function-body'` or `'program'`, default: `'program'`) + — output format to generate; + in most cases `'program'` should be used, it results in a whole program; + internally [`evaluate`][api-evaluate] uses `'function-body'` to compile to + code that can be passed to [`run`][api-run]; + in some cases, you might want to do what `evaluate` does in separate steps + yourself, such as when compiling on the server and running on the client. + +
Expand example + + With a module `example.js`: + + ```tsx + import {compile} from '@mdx-js/mdx' + + const code = 'export const no = 3.14\n\n# hi {no}' + + console.log(String(await compile(code, {outputFormat: 'program'}))) // Default. + console.log(String(await compile(code, {outputFormat: 'function-body'}))) + ``` + + …yields: + + ```tsx + /* @jsxRuntime automatic @jsxImportSource react */ + import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' + export const no = 3.14 + function _createMdxContent(props) { /* … */ } + export default function MDXContent(props = {}) { /* … */ } + ``` + + ```tsx + const {Fragment: _Fragment, jsx: _jsx} = arguments[0] + const no = 3.14 + function _createMdxContent(props) { /* … */ } + function MDXContent(props = {}) { /* … */ } + return {no, default: MDXContent} + ``` + + The `'program'` format will use import statements to import the runtime (and + optionally provider) and use an export statement to yield the `MDXContent` + component. + + The `'function-body'` format will get the runtime (and optionally provider) from + `arguments[0]`, rewrite export statements, and use a return statement to yield + what was exported. + Normally, this output format will throw on `import` (and `export … from`) + statements, but you can support them by setting `useDynamicImport`. + +
+* `mdExtensions` (`Array`, default: `['.md', '.markdown', '.mdown', + '.mkdn', '.mkd', '.mdwn', '.mkdown', '.ron']`) + — list of markdown extensions, with dot + affects [§ Integrations][integrations] +* `mdxExtensions` (`Array`, default: `['.mdx']`) + — list of MDX extensions, with dot; + affects [§ Integrations][integrations] +* `pragma` (`string`, default: `'React.createElement'`) + — pragma for JSX, used in the classic runtime as an identifier for function + calls: `` to `React.createElement('x')`; + when changing this, you should also define `pragmaFrag` and + `pragmaImportSource` too + +
Expand example + + If `file` is the contents of `example.mdx` from [§ Use][use], then: + + ```tsx + compile(file, { + jsxRuntime: 'classic', + pragma: 'preact.createElement', + pragmaFrag: 'preact.Fragment', + pragmaImportSource: 'preact/compat' + }) + ``` + + …yields this difference: + + ```diff + -/* @jsxRuntime classic @jsx React.createElement @jsxFrag React.Fragment */ + -import React from 'react' + +/* @jsxRuntime classic @jsx preact.createElement @jsxFrag preact.Fragment */ + +import preact from 'preact/compat' + + export function Thing() { + - return React.createElement(React.Fragment, null, 'World!') + + return preact.createElement(preact.Fragment, null, 'World!') + } + … + ``` + +
+* `pragmaFrag` (`string`, default: `'React.Fragment'`) + — pragma for fragment symbol, used in the classic runtime as an identifier + for unnamed calls: `<>` to `React.createElement(React.Fragment)`; + when changing this, you should also define `pragma` and `pragmaImportSource` + too +* `pragmaImportSource` (`string`, default: `'react'`) + — where to import the identifier of `pragma` from, used in the classic + runtime; + to illustrate, when `pragma` is `'a.b'` and `pragmaImportSource` is `'c'` + the following will be generated: `import a from 'c'` and things such as + `a.b('h1', {})`; + when changing this, you should also define `pragma` and `pragmaFrag` too +* `providerImportSource` (`string`, optional, example: `'@mdx-js/react'`) + — place to import a provider from; + normally it’s used for runtimes that support context (React, Preact), but + it can be used to inject components into the compiled code; + the module must export and identifier `useMDXComponents` which is called + without arguments to get an object of components (see + [`UseMdxComponents`][api-use-mdx-components]) + +
Expand example + + If `file` is the contents of `example.mdx` from [§ Use][use], then: + + ```tsx + compile(file, {providerImportSource: '@mdx-js/react'}) + ``` + + …yields this difference: + + ```diff + /* @jsxRuntime automatic @jsxImportSource react */ + import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' + +import {useMDXComponents as _provideComponents} from '@mdx-js/react' + + export function Thing() { + return _jsx(_Fragment, {children: 'World'}) + } + + function _createMdxContent(props) { + const _components = { + h1: 'h1', + + ..._provideComponents(), + ...props.components + } + return _jsxs(_components.h1, {children: ['Hello ', _jsx(Thing, {})]}) + } + + export default function MDXContent(props = {}) { + - const {wrapper: MDXLayout} = props.components || {} + + const {wrapper: MDXLayout} = { + + ..._provideComponents(), + + ...props.components + + } + + return MDXLayout + ? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {})}) + : _createMdxContent() + ``` + +
+* `recmaPlugins` ([`PluggableList` from `unified`][unified-pluggable-list], + optional) + — list of recma plugins; + this is a new ecosystem, currently in beta, to transform [esast][] trees + (JavaScript) + +
Expand example + + ```tsx + import recmaMdxIsMdxComponent from 'recma-mdx-is-mdx-component' + + await compile(file, {recmaPlugins: [recmaMdxIsMdxComponent]}) + ``` + +
+* `rehypePlugins` ([`PluggableList` from `unified`][unified-pluggable-list], + optional) + — list of [rehype plugins][rehype-plugins] + +
Expand example + + ```tsx + import rehypeKatex from 'rehype-katex' // Render math with KaTeX. + import remarkMath from 'remark-math' // Support math like `$so$`. + + await compile(file, {rehypePlugins: [rehypeKatex], remarkPlugins: [remarkMath]}) + + await compile(file, { + // A plugin with options: + rehypePlugins: [[rehypeKatex, {strict: true, throwOnError: true}]], + remarkPlugins: [remarkMath] + }) + ``` + +
+* `remarkPlugins` ([`PluggableList` from `unified`][unified-pluggable-list], + optional) + — list of [remark plugins][remark-plugins] + +
Expand example + + ```tsx + import remarkFrontmatter from 'remark-frontmatter' // YAML and such. + import remarkGfm from 'remark-gfm' // Tables, footnotes, strikethrough, task lists, literal URLs. + + await compile(file, {remarkPlugins: [remarkGfm]}) // One plugin. + await compile(file, {remarkPlugins: [[remarkFrontmatter, 'toml']]}) // A plugin with options. + await compile(file, {remarkPlugins: [remarkGfm, remarkFrontmatter]}) // Two plugins. + await compile(file, {remarkPlugins: [[remarkGfm, {singleTilde: false}], remarkFrontmatter]}) // Two plugins, first w/ options. + ``` + +
+* `remarkRehypeOptions` ([`Options` from + `remark-rehype`][remark-rehype-options], optional) + — options to pass through to `remark-rehype`; + the option `allowDangerousHtml` will always be set to `true` and the MDX + nodes (see [`nodeTypes`][api-node-types]) are passed through; + In particular, you might want to pass configuration for footnotes if your + content is not in English + +
Expand example + + ```tsx + compile({value: '…'}, {remarkRehypeOptions: {clobberPrefix: 'comment-1'}}) + ``` + +
+ +* `stylePropertyNameCase` (`'css'` or `'dom`, default: `'dom'`) + — casing to use for property names in `style` objects; + CSS casing is for example `background-color` and `-webkit-line-clamp`; + DOM casing is for example `backgroundColor` and `WebkitLineClamp`; + for JSX components written in MDX, the author has to be aware of which + framework they use and write code accordingly; + for AST nodes generated by this project, this option configures it +* `tableCellAlignToStyle` (`boolean`, default: `true`) + — turn obsolete `align` props on `td` and `th` into CSS `style` props +* `useDynamicImport` (`boolean`, default: `false`) + — whether to compile to dynamic import expressions when `outputFormat` is + `'function-body'`; + so, it will turn import statements (`import {x} from 'y'`) into dynamic + import expressions (`const {x} = await import('y')`); + import statements only work at the top level of modules but import + expressions are available inside function bodies; + you should probably set `baseUrl` too. + +
Expand example + + Say we have a couple modules: + + ```tsx + // meta.js: + export const title = 'World' + + // numbers.js: + export const no = 3.14 + + // example.js: + import {compile} from '@mdx-js/mdx' + + const code = `import {name} from './meta.js' + export {no} from './numbers.js' + + # hi {name}!` -
-Expand example + console.log(String(await compile(code, {outputFormat: 'function-body', useDynamicImport: true}))) + ``` -Assuming the contents of `example.mdx` from [§ Use][use] was in `file`, then: + …now running `node example.js` yields: + + ```tsx + const {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0] + const {name} = await import('./meta.js') + const {no} = await import('./numbers.js') + function _createMdxContent(props) { /* … */ } + function MDXContent(props = {}) { /* … */ } + return {no, default: MDXContent} + ``` + +
+ +### `RunOptions` + +Configuration to run compiled code (TypeScript type). + +`Fragment`, `jsx`, and `jsxs` are used when the code is compiled in production +mode (`development: false`). +`Fragment` and `jsxDEV` are used when compiled in development mode +(`development: true`). +`useMDXComponents` is used when the code is compiled with +`providerImportSource: '#'` (the exact value of this compile option doesn’t +matter). + +###### Fields + +* `Fragment` ([`Fragment`][api-fragment], **required**) + — symbol to use for fragments +* `jsx` ([`Jsx`][api-jsx], optional) + — function to generate an element with static children in production mode +* `jsxDEV` ([`JsxDEV`][api-jsx-dev], optional) + — function to generate an element in development mode +* `jsxs` ([`Jsx`][api-jsx], optional) + — function to generate an element with dynamic children in production mode +* `useMDXComponents` ([`UseMdxComponents`][api-use-mdx-components], optional) + — function to get components from context + +###### Examples + +A `/jsx-runtime` module will expose `Fragment`, `jsx`, and `jsxs`: ```tsx import * as runtime from 'react/jsx-runtime' -import {evaluate} from '@mdx-js/mdx' - -console.log(await evaluate(file, runtime)) -``` - -…yields: -```tsx -{Thing: [Function: Thing], default: [Function: MDXContent]} -``` - -
- -###### Note: Performance - -Compiling (and running) MDX takes time. -If you’re live-rendering a string of MDX that often changes using a virtual DOM -based framework (such as React), one performance improvement is to call the -`MDXContent` component yourself. -The reason is that the `evaluate` creates a new function each time, which cannot -be diffed: - -```diff - const {default: MDXContent} = await evaluate('…') +const {default: Content} = await evaluate('# hi', {...runtime, ...otherOptions}) -- -+MDXContent(props) ``` -### `evaluateSync(file, options)` - -> ☢️ **Danger**: It’s called **evaluate** because it `eval`s JavaScript. - -Compile and run MDX. -Synchronous version of [`evaluate`][eval]. -When possible please use the async `evaluate`. - -### `run(functionBody, options)` - -> ☢️ **Danger**: This `eval`s JavaScript. - -Run MDX compiled as [`options.outputFormat: 'function-body'`][outputformat]. - -###### `options` - -You can pass `Fragment`, `jsx`, `jsxs`, and `jsxDEV`, from an automatic JSX -runtime as `options`. -You can also pass `useMDXComponents` from a provider in options if the MDX is -compiled with `options.providerImportSource: '#'` (the exact value of this -compile option doesn’t matter). -All other options have to be passed to `compile` instead. - -###### Returns - -`Promise` — See `evaluate` - -
-Expand example - -On the server: +A `/jsx-dev-runtime` module will expose `Fragment` and `jsxDEV`: ```tsx -import {compile} from '@mdx-js/mdx' +import * as runtime from 'react/jsx-dev-runtime' -const code = String(await compile('# hi', {outputFormat: 'function-body'})) -// To do: send `code` to the client somehow. +const {default: Content} = await evaluate('# hi', {development: true, ...runtime, ...otherOptions}) ``` -On the client: +Our providers will expose `useMDXComponents`: ```tsx +import * as provider from '@mdx-js/react' import * as runtime from 'react/jsx-runtime' -import {run} from '@mdx-js/mdx' -const code = '' // To do: get `code` from server somehow. - -const {default: Content} = await run(code, runtime) -``` - -…yields: - -```tsx -[Function: MDXContent] +const {default: Content} = await evaluate('# hi', {...provider, ...runtime, ...otherOptions}) ``` -
+### `UseMdxComponents` -### `runSync(functionBody, options)` +Get components from context (TypeScript type). -> ☢️ **Danger**: This `eval`s JavaScript. +###### Parameters -Run MDX. -Synchronous version of [`run`][run]. -When possible please use the async `run`. +There are no parameters. -### `createProcessor(options)` - -Create a unified processor to compile MDX to JS. -Has the same options as [`compile`][compile], but returns a configured -[`processor`][processor]. +###### Returns -Note that `format: 'detect'` does not work here: only `'md'` or `'mdx'` are -allowed (and `'mdx'` is the default). +Components ([`MDXComponents` from `mdx/types.js`][mdx-types-components]). ## Types This package is fully typed with [TypeScript][]. +It exports the additional types +[`CompileOptions`][api-compile-options], +[`EvaluateOptions`][api-evaluate-options], +[`Fragment`][api-fragment], +[`Jsx`][api-jsx], +[`JsxDev`][api-jsx-dev], +[`ProcessorOptions`][api-processor-options], +[`RunOptions`][api-run-options], and +[`UseMdxComponents`][api-use-mdx-components]. + +For types of evaluated MDX to work, make sure the TypeScript `JSX` namespace is +typed. +This is done by installing and using the types of your framework, such as +[`@types/react`](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). See [§ Types][types] on our website for information. -Additional `CompileOptions`, `EvaluateOptions`, and `ProcessorOptions` types -are exported, which represents acceptable configuration for their respective -methods. - ## Architecture To understand what this project does, it’s very important to first understand @@ -1083,9 +1171,9 @@ abide by its terms. [downloads]: https://www.npmjs.com/package/@mdx-js/mdx -[size-badge]: https://img.shields.io/bundlephobia/minzip/@mdx-js/mdx.svg +[size-badge]: https://img.shields.io/bundlejs/size/@mdx-js/mdx -[size]: https://bundlephobia.com/result?p=@mdx-js/mdx +[size]: https://bundlejs.com/?q=@mdx-js/mdx [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg @@ -1121,8 +1209,6 @@ abide by its terms. [create-processor]: #createprocessoroptions -[buffer]: https://nodejs.org/api/buffer.html - [source-map]: https://github.com/mozilla/source-map [vfile]: https://github.com/vfile/vfile @@ -1181,8 +1267,54 @@ abide by its terms. [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c +[esmsh]: https://esm.sh + [types]: https://mdxjs.com/getting-started/#types [security]: https://mdxjs.com/getting-started/#security [typescript]: https://www.typescriptlang.org + +[mdx-types-components]: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/mdx/types.d.ts#L65 + +[mdx-types-module]: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/mdx/types.d.ts#L101 + +[remark-rehype-options]: https://github.com/remarkjs/remark-rehype#options + +[unified-pluggable-list]: https://github.com/unifiedjs/unified#pluggablelist + +[unified-processor]: https://github.com/unifiedjs/unified#processor + +[vfile-compatible]: https://github.com/vfile/vfile#compatible + +[api-compile]: #compilefile-options + +[api-compile-options]: #compileoptions + +[api-compile-sync]: #compilesyncfile-options + +[api-create-processor]: #createprocessoroptions + +[api-evaluate]: #evaluatefile-options + +[api-evaluate-options]: #evaluateoptions + +[api-evaluate-sync]: #evaluatesyncfile-options + +[api-fragment]: #fragment + +[api-jsx]: #jsx + +[api-jsx-dev]: #jsxdev + +[api-node-types]: #nodetypes + +[api-processor-options]: #processoroptions + +[api-run]: #runcode-options + +[api-run-options]: #runoptions + +[api-run-sync]: #runsynccode-options + +[api-use-mdx-components]: #usemdxcomponents diff --git a/packages/mdx/test/evaluate.js b/packages/mdx/test/evaluate.js index e757f2415..38551857c 100644 --- a/packages/mdx/test/evaluate.js +++ b/packages/mdx/test/evaluate.js @@ -1,6 +1,7 @@ /** - * @typedef {import('../lib/util/resolve-evaluate-options.js').RuntimeDevelopment} RuntimeDevelopment - * @typedef {import('../lib/util/resolve-evaluate-options.js').RuntimeProduction} RuntimeProduction + * @typedef {import('../lib/util/resolve-evaluate-options.js').Fragment} Fragment + * @typedef {import('../lib/util/resolve-evaluate-options.js').Jsx} Jsx + * @typedef {import('../lib/util/resolve-evaluate-options.js').JsxDev} JsxDev */ import assert from 'node:assert/strict' @@ -12,10 +13,10 @@ import * as runtime_ from 'react/jsx-runtime' import * as devRuntime_ from 'react/jsx-dev-runtime' import React from 'react' -/** @type {RuntimeProduction} */ +/** @type {{Fragment: Fragment, jsx: Jsx, jsxs: Jsx}} */ // @ts-expect-error: the automatic react runtime is untyped. const runtime = runtime_ -/** @type {RuntimeDevelopment} */ +/** @type {{Fragment: Fragment, jsxDEV: JsxDev}} */ // @ts-expect-error: the automatic dev react runtime is untyped. const devRuntime = devRuntime_ diff --git a/packages/node-loader/index.js b/packages/node-loader/index.js index 877ce08f5..a13d7072a 100644 --- a/packages/node-loader/index.js +++ b/packages/node-loader/index.js @@ -7,4 +7,8 @@ import {createLoader} from './lib/index.js' const defaultLoader = createLoader() export {createLoader} from './lib/index.js' + +/** + * Load `file:` URLs to MD(X) files. + */ export const load = defaultLoader.load diff --git a/packages/node-loader/lib/index.js b/packages/node-loader/lib/index.js index 89d59179d..890450004 100644 --- a/packages/node-loader/lib/index.js +++ b/packages/node-loader/lib/index.js @@ -1,5 +1,21 @@ /** - * @typedef {import('@mdx-js/mdx').CompileOptions} Options + * @typedef {import('node:module').LoadHookContext} LoadHookContext + * @typedef {import('node:module').LoadFnOutput} LoadFnOutput + * @typedef {import('node:module').LoadHook} LoadHookType + * @typedef {import('@mdx-js/mdx').CompileOptions} CompileOptions + */ + +/** + * @typedef {Parameters[2]} NextLoad + * Next. + * + * @typedef {Omit} Options + * Configuration. + * + * Options are the same as [`compile` from `@mdx-js/mdx`][mdx-options] + * with the exception that the `development` option is supported based on how you + * configure webpack. + * You cannot pass it manually. */ import fs from 'node:fs/promises' @@ -9,12 +25,12 @@ import {VFile} from 'vfile' import {development as defaultDevelopment} from '#condition' /** - * Create a loader to handle markdown and MDX. + * Create Node.js hooks to handle markdown and MDX. * * @param {Readonly | null | undefined} [options] * Configuration (optional). * @returns - * Loader. + * Node.js hooks. */ export function createLoader(options) { const options_ = options || {} @@ -27,16 +43,19 @@ export function createLoader(options) { return {load} /** + * Load `file:` URLs to MD(X) files. + * * @param {string} href * URL. - * @param {unknown} context + * @param {LoadHookContext} context * Context. - * @param {Function} defaultLoad - * Default `load` function. - * @returns + * @param {NextLoad} nextLoad + * Next or default `load` function. + * @returns {Promise} * Result. + * @satisfies {LoadHookType} */ - async function load(href, context, defaultLoad) { + async function load(href, context, nextLoad) { const url = new URL(href) if (url.protocol === 'file:' && regex.test(url.pathname)) { @@ -46,6 +65,6 @@ export function createLoader(options) { return {format: 'module', shortCircuit: true, source: String(file)} } - return defaultLoad(href, context, defaultLoad) + return nextLoad(href, context) } } diff --git a/packages/node-loader/readme.md b/packages/node-loader/readme.md index 84674640b..7f04a5eaf 100644 --- a/packages/node-loader/readme.md +++ b/packages/node-loader/readme.md @@ -7,13 +7,10 @@ [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat] -Node loader for MDX. +Node.js hooks (also knows as loaders) for MDX. -> 💡 **Experiment**: this is an experimental package that might not work -> well and might change in minor releases. - ## Contents * [What is this?](#what-is-this) @@ -29,38 +26,27 @@ Node loader for MDX. ## What is this? -This package is a Node ESM loader to support MDX. -[ESM loaders][loader] are an experimental feature in Node, slated to change. -They let projects “hijack” imports to do all sorts of fancy things, in this -case it let’s you `import` MD(X) files. +This package contains Node.js hooks to add support for importing MDX files. +Node *Customization Hooks* are currently a release candidate. ## When should I use this? This integration is useful if you’re using Node and want to import MDX files from the file system. -If you’re using a bundler (webpack, Rollup, esbuild), or a site builder (Gatsby, -Next.js) or build system (Vite, WMR) which comes with a bundler, you’re better -off using another integration: see -[§ Integrations][integrations]. +If you’re using a bundler (webpack, Rollup, esbuild), or a site builder +(Next.js) or build system (Vite) which comes with a bundler, you can instead +another integration: see [§ Integrations][integrations]. ## Install -This package is [ESM only][esm]: -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install @mdx-js/node-loader ``` -[yarn][]: - -```sh -yarn add @mdx-js/node-loader -``` - ## Use Say we have an MDX document, `example.mdx`: @@ -83,10 +69,10 @@ import Content from './example.mdx' console.log(renderToStaticMarkup(React.createElement(Content))) ``` -…then running that with: +…then running with: ```sh -node --experimental-loader=@mdx-js/node-loader example.js +node --loader=@mdx-js/node-loader example.js ``` …yields: @@ -95,47 +81,71 @@ node --experimental-loader=@mdx-js/node-loader example.js

Hello, World!

``` -## API +> **Note**: if you use Node 18 and lower, then you can ignore the following +> warning: +> +> ```txt +> (node:20718) ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any > time +> (Use `node --trace-warnings ...` to show where the warning was created) +> ``` + +> **Note**: if you use Node 20 and higher, then you get the following warning: +> +> ```txt +> (node:20908) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use > `register()`: +> --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from > "node:url"; register("%40mdx-js/node-loader", pathToFileURL("./"));' +> ``` +> +> You can solve that by adding a `register.js` file: +> +> ```tsx +> import {register} from 'node:module' +> +> register('@mdx-js/node-loader', import.meta.url) +> ``` +> +> …and running `node --import ./register.js example.js` instead. -> 💡 **Experiment**: this is an experimental package that might not work -> well and might change in minor releases. +## API -This package exports a Node [ESM loader][loader]. -It also exports the following identifier: `createLoader`. +This package export the identifiers [`createLoader`][api-create-loader] and +[`load`][api-load]. +There is no default export. ### `createLoader(options?)` -Create a Node ESM loader to compile MDX to JS. +Create Node.js hooks to handle markdown and MDX. -##### `options` +###### Parameters -`options` are the same as [`compile` from `@mdx-js/mdx`][options]. -One extra field is supported: +* `options` ([`Options`][api-options], optional) + — configuration -###### Example +###### Returns -`my-loader.js`: +Node.js hooks ([`{load}`][api-load]). -```tsx -import {createLoader} from '@mdx-js/node-loader' +### `load` -const {load} = createLoader(/* Options… */) +Load `file:` URLs to MD(X) files. -export {load} -``` +See [`load` in Node.js docs][node-load] for more info. + +### `Options` -This example can then be used with `node --experimental-loader=./my-loader.js`. +Configuration (TypeScript type). -Node itself does not yet support multiple loaders but it is possible to combine -multiple loaders with [`@node-loader/core`][node-loader-core]. +Options are the same as [`CompileOptions` from `@mdx-js/mdx`][compile-options] +with the exception that the `development` option is supported based on how you +configure webpack. +You cannot pass it manually. ## Types This package is fully typed with [TypeScript][]. +It exports the additional type [`Options`][api-options]. See [§ Types][types] on our website for information. -An `Options` type is exported, which represents acceptable configuration. - ## Security See [§ Security][security] on our website for information. @@ -199,8 +209,16 @@ abide by its terms. [security]: https://mdxjs.com/getting-started/#security -[options]: https://mdxjs.com/packages/mdx/#compilefile-options - [typescript]: https://www.typescriptlang.org [node-loader-core]: https://github.com/node-loader/node-loader-core + +[compile-options]: https://mdxjs.com/packages/mdx/#compileoptions + +[node-load]: https://nodejs.org/api/module.html#loadurl-context-nextload + +[api-create-loader]: #createloaderoptions + +[api-load]: #load + +[api-options]: #options diff --git a/packages/preact/lib/index.js b/packages/preact/lib/index.js index 8bb5b9ae0..a7d27ed75 100644 --- a/packages/preact/lib/index.js +++ b/packages/preact/lib/index.js @@ -1,38 +1,41 @@ /** - * @typedef {import('mdx/types.js').MDXComponents} Components + * @typedef {import('mdx/types.js').MDXComponents} MDXComponents + * @typedef {import('preact').Component} Component * @typedef {import('preact').ComponentChildren} ComponentChildren */ /** * @callback MergeComponents * Custom merge function. - * @param {Readonly} currentComponents + * @param {Readonly} currentComponents * Current components from the context. - * @returns {Components} - * Merged components. + * @returns {MDXComponents} + * Additional components. * * @typedef Props - * Configuration. - * @property {Readonly | MergeComponents | null | undefined} [components] - * Mapping of names for JSX components to Preact components (optional). - * @property {boolean | null | undefined} [disableParentContext=false] - * Turn off outer component context (default: `false`). + * Configuration for `MDXProvider`. * @property {ComponentChildren | null | undefined} [children] * Children (optional). + * @property {Readonly | MergeComponents | null | undefined} [components] + * Additional components to use or a function that creates them (optional). + * @property {boolean | null | undefined} [disableParentContext=false] + * Turn off outer component context (default: `false`). */ import {createContext, h} from 'preact' import {useContext} from 'preact/hooks' -const MDXContext = createContext({}) +/** @type {Readonly} */ +const emptyComponents = {} + +const MDXContext = createContext(emptyComponents) /** * Get current components from the MDX Context. * - * @param {Readonly | MergeComponents | null | undefined} [components] - * Additional components to use or a function that takes the current - * components and filters/merges/changes them (optional). - * @returns {Components} + * @param {Readonly | MergeComponents | null | undefined} [components] + * Additional components to use or a function that creates them (optional). + * @returns {MDXComponents} * Current components. */ export function useMDXComponents(components) { @@ -46,33 +49,31 @@ export function useMDXComponents(components) { return {...contextComponents, ...components} } -/** @type {Readonly} */ -const emptyObject = {} - /** - * Provider for MDX context + * Provider for MDX context. * * @param {Readonly} props * Props. * @returns {JSX.Element} * Element. + * @satisfies {Component} */ -export function MDXProvider({children, components, disableParentContext}) { - /** @type {Readonly} */ +export function MDXProvider(props) { + /** @type {Readonly} */ let allComponents - if (disableParentContext) { + if (props.disableParentContext) { allComponents = - typeof components === 'function' - ? components({}) - : components || emptyObject + typeof props.components === 'function' + ? props.components(emptyComponents) + : props.components || emptyComponents } else { - allComponents = useMDXComponents(components) + allComponents = useMDXComponents(props.components) } return h( MDXContext.Provider, {children: undefined, value: allComponents}, - children + props.children ) } diff --git a/packages/preact/readme.md b/packages/preact/readme.md index ba16ab9d8..3abd3a246 100644 --- a/packages/preact/readme.md +++ b/packages/preact/readme.md @@ -28,30 +28,36 @@ Preact context for MDX. ## What is this? -This package is a context based components provider for combining Preact with +This package is a *context* based components provider for combining Preact with MDX. ## When should I use this? -This package is not needed for MDX to work with Preact. +This package is **not needed** for MDX to work with Preact. See [¶ MDX provider in § Using MDX][use-provider] for when and how to use an MDX provider. ## Install -This package is [ESM only][esm]: -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install @mdx-js/preact ``` -[yarn][]: +In Deno with [`esm.sh`][esmsh]: -```sh -yarn add @mdx-js/preact +```tsx +import {MDXProvider} from 'https://esm.sh/@mdx-js/preact@2' +``` + +In browsers with [`esm.sh`][esmsh]: + +```html + ``` ## Use @@ -64,24 +70,29 @@ import Post from './post.mdx' // `@mdx-js/rollup`, and that it is configured with // `options.providerImportSource: '@mdx-js/preact'`. +/** @type {import('mdx/types.js').MDXComponents} */ const components = { - em: props => + em(props) { + return + } } - - - +console.log( + + + +) ``` -Note that you don’t have to use `MDXProvider` and can pass components -directly: - -```diff -- -- -- -+ -``` +> 👉 **Note**: you don’t have to use `MDXProvider` and can pass components +> directly: +> +> ```diff +> - +> - +> - +> + +> ``` See [¶ Preact in § Getting started][start-preact] for how to get started with MDX and Preact. @@ -90,60 +101,76 @@ provider. ## API -This package exports the following identifiers: `MDXProvider` and -`useMDXComponents`. +This package exports the identifiers [`MDXProvider`][api-mdx-provider] and +[`useMDXComponents`][api-use-mdx-components]. There is no default export. ### `MDXProvider(props?)` Provider for MDX context. -##### `props` +###### Parameters -Configuration (`Object`, optional). +* `props` ([`Props`][api-props]) + — configuration -###### `props.components` +##### Returns -Mapping of names for JSX components to Preact components -(`Record`, optional). +Element (`JSX.Element`). -###### `props.disableParentContext` +### `useMDXComponents(components?)` -Turn off outer component context (`boolean`, default: `false`). +Get current components from the MDX Context. -###### `props.children` +###### Parameters -Children (JSX elements, optional). +* `components` ([`MDXComponents` from `mdx/types.js`][mdx-types-components] + or [`MergeComponents`][api-merge-components], optional) + — additional components to use or a function that creates them -##### Returns +###### Returns -JSX element. +Current components ([`MDXComponents` from +`mdx/types.js`][mdx-types-components]). -### `useMDXComponents(components?)` +### `MergeComponents` -Get current components from the MDX Context. +Custom merge function (TypeScript type). -###### `components` +###### Parameters -Additional components (`Components`) to use or a function that takes the current -components and filters/merges/changes them (`(currentComponents: Components) => -Components`). +* `components` ([`MDXComponents` from `mdx/types.js`][mdx-types-components]) + — current components from the context ###### Returns -`Components`. +Additional components ([`MDXComponents` from +`mdx/types.js`][mdx-types-components]). + +### `Props` + +Configuration for `MDXProvider` (TypeScript type). + +###### Fields + +* `children` ([`ComponentChildren` from `preact`][preact-component-children], + optional) + — children +* `components` ([`MDXComponents` from `mdx/types.js`][mdx-types-components] + or [`MergeComponents`][api-merge-components], optional) + — additional components to use or a function that creates them +* `disableParentContext` (`boolean`, default: `false`) + — turn off outer component context ## Types This package is fully typed with [TypeScript][]. +It exports the additional types [`MergeComponents`][api-merge-components] and +[`Props`][api-props]. -To enable types for imported `.mdx`, `.md`, etcetera files, you should make sure -the TypeScript `JSX` namespace is typed. +For types to work, make sure the TypeScript `JSX` namespace is typed. This is done by installing and using the types of your framework, as in [`preact`](https://github.com/preactjs/preact). -Then you can install and use -[`@types/mdx`](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mdx), -which adds types to import statements of supported files. ## Security @@ -174,9 +201,9 @@ abide by its terms. [downloads]: https://www.npmjs.com/package/@mdx-js/preact -[size-badge]: https://img.shields.io/bundlephobia/minzip/@mdx-js/preact.svg +[size-badge]: https://img.shields.io/bundlejs/size/@mdx-js/preact -[size]: https://bundlephobia.com/result?p=@mdx-js/preact +[size]: https://bundlejs.com/?q=@mdx-js/preact [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg @@ -190,8 +217,6 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[yarn]: https://classic.yarnpkg.com/docs/cli/add/ - [contribute]: https://mdxjs.com/community/contribute/ [support]: https://mdxjs.com/community/support/ @@ -208,6 +233,20 @@ abide by its terms. [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c +[esmsh]: https://esm.sh + [security]: https://mdxjs.com/getting-started/#security [typescript]: https://www.typescriptlang.org + +[mdx-types-components]: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/mdx/types.d.ts#L65 + +[preact-component-children]: https://github.com/preactjs/preact/blob/main/src/index.d.ts#L53 + +[api-mdx-provider]: #mdxproviderprops + +[api-merge-components]: #mergecomponents + +[api-props]: #props + +[api-use-mdx-components]: #usemdxcomponentscomponents diff --git a/packages/preact/test/index.jsx b/packages/preact/test/index.jsx index 15633b1ea..7cb83498c 100644 --- a/packages/preact/test/index.jsx +++ b/packages/preact/test/index.jsx @@ -1,7 +1,8 @@ /* @jsxRuntime automatic @jsxImportSource preact */ /** - * @typedef {import('@mdx-js/mdx/internal-resolve-evaluate-options').RuntimeProduction} RuntimeProduction + * @typedef {import('@mdx-js/mdx/internal-resolve-evaluate-options').Fragment} Fragment + * @typedef {import('@mdx-js/mdx/internal-resolve-evaluate-options').Jsx} Jsx */ import assert from 'node:assert/strict' @@ -11,7 +12,9 @@ import {MDXProvider, useMDXComponents} from '@mdx-js/preact' import * as runtime_ from 'preact/jsx-runtime' import {render} from 'preact-render-to-string' -const runtime = /** @type {RuntimeProduction} */ (runtime_) +const runtime = /** @type {{Fragment: Fragment, jsx: Jsx, jsxs: Jsx}} */ ( + runtime_ +) test('@mdx-js/preact', async function (t) { await t.test('should expose the public api', async function () { diff --git a/packages/react/lib/index.js b/packages/react/lib/index.js index 3d0bc6693..641d135ee 100644 --- a/packages/react/lib/index.js +++ b/packages/react/lib/index.js @@ -1,37 +1,40 @@ /** - * @typedef {import('mdx/types.js').MDXComponents} Components + * @typedef {import('mdx/types.js').MDXComponents} MDXComponents + * @typedef {import('react').Component<{}, {}, unknown>} Component * @typedef {import('react').ReactNode} ReactNode */ /** * @callback MergeComponents * Custom merge function. - * @param {Readonly} currentComponents + * @param {Readonly} currentComponents * Current components from the context. - * @returns {Components} - * Merged components. + * @returns {MDXComponents} + * Additional components. * * @typedef Props - * Configuration. - * @property {Readonly | MergeComponents | null | undefined} [components] - * Mapping of names for JSX components to React components (optional). - * @property {boolean | null | undefined} [disableParentContext=false] - * Turn off outer component context (default: `false`). + * Configuration for `MDXProvider`. * @property {ReactNode | null | undefined} [children] * Children (optional). + * @property {Readonly | MergeComponents | null | undefined} [components] + * Additional components to use or a function that creates them (optional). + * @property {boolean | null | undefined} [disableParentContext=false] + * Turn off outer component context (default: `false`). */ import React from 'react' -const MDXContext = React.createContext({}) +/** @type {Readonly} */ +const emptyComponents = {} + +const MDXContext = React.createContext(emptyComponents) /** * Get current components from the MDX Context. * - * @param {Readonly | MergeComponents | null | undefined} [components] - * Additional components to use or a function that takes the current - * components and filters/merges/changes them (optional). - * @returns {Components} + * @param {Readonly | MergeComponents | null | undefined} [components] + * Additional components to use or a function that creates them (optional). + * @returns {MDXComponents} * Current components. */ export function useMDXComponents(components) { @@ -51,33 +54,31 @@ export function useMDXComponents(components) { ) } -/** @type {Readonly} */ -const emptyObject = {} - /** - * Provider for MDX context + * Provider for MDX context. * * @param {Readonly} props * Props. * @returns {JSX.Element} * Element. + * @satisfies {Component} */ -export function MDXProvider({children, components, disableParentContext}) { - /** @type {Readonly} */ +export function MDXProvider(props) { + /** @type {Readonly} */ let allComponents - if (disableParentContext) { + if (props.disableParentContext) { allComponents = - typeof components === 'function' - ? components({}) - : components || emptyObject + typeof props.components === 'function' + ? props.components(emptyComponents) + : props.components || emptyComponents } else { - allComponents = useMDXComponents(components) + allComponents = useMDXComponents(props.components) } return React.createElement( MDXContext.Provider, {value: allComponents}, - children + props.children ) } diff --git a/packages/react/readme.md b/packages/react/readme.md index c1e32853d..6093148cd 100644 --- a/packages/react/readme.md +++ b/packages/react/readme.md @@ -28,30 +28,36 @@ React context for MDX. ## What is this? -This package is a context based components provider for combining React with +This package is a *context* based components provider for combining React with MDX. ## When should I use this? -This package is not needed for MDX to work with React. +This package is **not needed** for MDX to work with React. See [¶ MDX provider in § Using MDX][use-provider] for when and how to use an MDX provider. ## Install -This package is [ESM only][esm]: -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only][esm]. +In Node.js (version 16+), install with [npm][]: ```sh npm install @mdx-js/react ``` -[yarn][]: +In Deno with [`esm.sh`][esmsh]: -```sh -yarn add @mdx-js/react +```tsx +import {MDXProvider} from 'https://esm.sh/@mdx-js/react@2' +``` + +In browsers with [`esm.sh`][esmsh]: + +```html + ``` ## Use @@ -64,24 +70,29 @@ import Post from './post.mdx' // `@mdx-js/rollup`, and that it is configured with // `options.providerImportSource: '@mdx-js/react'`. +/** @type {import('mdx/types.js').MDXComponents} */ const components = { - em: props => + em(props) { + return + } } - - - +console.log( + + + +) ``` -Note that you don’t have to use `MDXProvider` and can pass components -directly: - -```diff -- -- -- -+ -``` +> 👉 **Note**: you don’t have to use `MDXProvider` and can pass components +> directly: +> +> ```diff +> - +> - +> - +> + +> ``` See [¶ React in § Getting started][start-react] for how to get started with MDX and React. @@ -90,60 +101,76 @@ provider. ## API -This package exports the following identifiers: `MDXProvider` and -`useMDXComponents`. +This package exports the identifiers [`MDXProvider`][api-mdx-provider] and +[`useMDXComponents`][api-use-mdx-components]. There is no default export. ### `MDXProvider(props?)` Provider for MDX context. -##### `props` +###### Parameters -Configuration (`Object`, optional). +* `props` ([`Props`][api-props]) + — configuration -###### `props.components` +##### Returns -Mapping of names for JSX components to React components -(`Record`, optional). +Element (`JSX.Element`). -###### `props.disableParentContext` +### `useMDXComponents(components?)` -Turn off outer component context (`boolean`, default: `false`). +Get current components from the MDX Context. -###### `props.children` +###### Parameters -Children (JSX elements, optional). +* `components` ([`MDXComponents` from `mdx/types.js`][mdx-types-components] + or [`MergeComponents`][api-merge-components], optional) + — additional components to use or a function that creates them -##### Returns +###### Returns -JSX element. +Current components ([`MDXComponents` from +`mdx/types.js`][mdx-types-components]). -### `useMDXComponents(components?)` +### `MergeComponents` -Get current components from the MDX Context. +Custom merge function (TypeScript type). -###### `components` +###### Parameters -Additional components (`Components`) to use or a function that takes the current -components and filters/merges/changes them (`(currentComponents: Components) => -Components`). +* `components` ([`MDXComponents` from `mdx/types.js`][mdx-types-components]) + — current components from the context ###### Returns -`Components`. +Additional components ([`MDXComponents` from +`mdx/types.js`][mdx-types-components]). + +### `Props` + +Configuration for `MDXProvider` (TypeScript type). + +###### Fields + +* `children` ([`ReactNode` from `react`][react-node], + optional) + — children +* `components` ([`MDXComponents` from `mdx/types.js`][mdx-types-components] + or [`MergeComponents`][api-merge-components], optional) + — additional components to use or a function that creates them +* `disableParentContext` (`boolean`, default: `false`) + — turn off outer component context ## Types This package is fully typed with [TypeScript][]. +It exports the additional types [`MergeComponents`][api-merge-components] and +[`Props`][api-props]. -To enable types for imported `.mdx`, `.md`, etcetera files, you should make sure -the TypeScript `JSX` namespace is typed. -This is done by installing and using the types of your framework, such as +For types to work, make sure the TypeScript `JSX` namespace is typed. +This is done by installing and using the types of your framework, as in [`@types/react`](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). -Then you can install and use -[`@types/mdx`](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mdx), -which adds types to import statements of supported files. ## Security @@ -174,9 +201,9 @@ abide by its terms. [downloads]: https://www.npmjs.com/package/@mdx-js/react -[size-badge]: https://img.shields.io/bundlephobia/minzip/@mdx-js/react.svg +[size-badge]: https://img.shields.io/bundlejs/size/@mdx-js/react -[size]: https://bundlephobia.com/result?p=@mdx-js/react +[size]: https://bundlejs.com/?q=@mdx-js/react [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg @@ -190,8 +217,6 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install -[yarn]: https://classic.yarnpkg.com/docs/cli/add/ - [contribute]: https://mdxjs.com/community/contribute/ [support]: https://mdxjs.com/community/support/ @@ -202,12 +227,26 @@ abide by its terms. [vercel]: https://vercel.com -[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c - [start-react]: https://mdxjs.com/getting-started/#react [use-provider]: https://mdxjs.com/docs/using-mdx/#mdx-provider +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +[esmsh]: https://esm.sh + [security]: https://mdxjs.com/getting-started/#security [typescript]: https://www.typescriptlang.org + +[mdx-types-components]: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/mdx/types.d.ts#L65 + +[react-node]: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/react/index.d.ts#L244 + +[api-mdx-provider]: #mdxproviderprops + +[api-merge-components]: #mergecomponents + +[api-props]: #props + +[api-use-mdx-components]: #usemdxcomponentscomponents diff --git a/packages/react/test/index.jsx b/packages/react/test/index.jsx index 5ca522c7c..9ae580302 100644 --- a/packages/react/test/index.jsx +++ b/packages/react/test/index.jsx @@ -1,5 +1,6 @@ /** - * @typedef {import('@mdx-js/mdx/internal-resolve-evaluate-options').RuntimeProduction} RuntimeProduction + * @typedef {import('@mdx-js/mdx/internal-resolve-evaluate-options').Fragment} Fragment + * @typedef {import('@mdx-js/mdx/internal-resolve-evaluate-options').Jsx} Jsx */ import assert from 'node:assert/strict' @@ -10,7 +11,7 @@ import React from 'react' import * as runtime_ from 'react/jsx-runtime' import {renderToString} from 'react-dom/server' -const runtime = /** @type {RuntimeProduction} */ ( +const runtime = /** @type {{Fragment: Fragment, jsx: Jsx, jsxs: Jsx}} */ ( /** @type {unknown} */ (runtime_) ) diff --git a/packages/remark-mdx/lib/index.js b/packages/remark-mdx/lib/index.js index 89428d457..07da89dda 100644 --- a/packages/remark-mdx/lib/index.js +++ b/packages/remark-mdx/lib/index.js @@ -16,8 +16,8 @@ import {mdxjs} from 'micromark-extension-mdxjs' const emptyOptions = {} /** - * Plugin to support MDX (import/exports: `export {x} from 'y'`; expressions: - * `{1 + 1}`; and JSX: `