mirror of
https://github.com/mdx-js/mdx.git
synced 2024-10-05 20:07:37 +03:00
Add rehype-twoslash
* check most examples with typescript * add special twoslash directives to code blocks so that twoslash understand partial examples * add dependencies for these examples, so typescript can check them * remove theme-ui docs here, their site has info, link to it
This commit is contained in:
parent
b749d38fd3
commit
11ac939bc3
@ -297,7 +297,6 @@ pre code {
|
||||
}
|
||||
|
||||
pre code,
|
||||
.hljs,
|
||||
.frame-body,
|
||||
.frame-tab-item-selected {
|
||||
background-color: #fafafa !important; /* Color from one-light */
|
||||
@ -583,7 +582,6 @@ button.success {
|
||||
|
||||
.navigation .icon {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
width: auto;
|
||||
height: calc(1em + 1ex);
|
||||
}
|
||||
@ -660,9 +658,6 @@ button.success {
|
||||
padding-inline-start: calc(0.5 * (1em + 1ex));
|
||||
}
|
||||
|
||||
.nav-description {
|
||||
}
|
||||
|
||||
.skip-to-navigation {
|
||||
inset-block-start: 0;
|
||||
inset-inline-start: 0;
|
||||
@ -837,11 +832,13 @@ button.success {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.frame-tab-bar + pre {
|
||||
.frame-tab-bar + pre,
|
||||
.frame-tab-bar + .highlight > pre {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
|
||||
.frame-tab-bar + pre code {
|
||||
.frame-tab-bar + pre code,
|
||||
.frame-tab-bar + .highlight > pre code {
|
||||
--squircle-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
@ -946,6 +943,55 @@ details[open] {
|
||||
padding: calc(1em + 1ex);
|
||||
}
|
||||
|
||||
.rehype-twoslash-completion-deprecated {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.rehype-twoslash-popover-target {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.highlight:is(:hover, :focus-within) .rehype-twoslash-popover-target {
|
||||
background-color: var(--gray-2);
|
||||
}
|
||||
|
||||
/* Wavy underline for errors. */
|
||||
.rehype-twoslash-error-target {
|
||||
background-repeat: repeat-x;
|
||||
background-position: bottom left;
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 3" enable-background="new 0 0 6 3" height="3" width="6"><g fill="%23c94824"><polygon points="5.5,0 2.5,3 1.1,3 4.1,0"/><polygon points="4,0 6,2 6,0.6 5.4,0"/><polygon points="0,2 1,3 2.4,3 0,0.6"/></g></svg>');
|
||||
}
|
||||
|
||||
/* The content that will be shown in the tooltip. */
|
||||
.rehype-twoslash-popover {
|
||||
position: absolute;
|
||||
max-width: calc(45 * (1em + 1ex));
|
||||
padding: calc(0.5 * (1em + 1ex));
|
||||
margin: 0;
|
||||
background-color: var(--bg);
|
||||
border: 1px solid var(--gray-2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* No padding if we have a padded code block (and perhaps more blocks) */
|
||||
.rehype-twoslash-popover:has(.rehype-twoslash-popover-code) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rehype-twoslash-popover-code {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rehype-twoslash-popover-code > code {
|
||||
mask-image: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.rehype-twoslash-popover-description {
|
||||
background-color: var(--bg);
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--white: #f0f6fc;
|
||||
@ -1038,6 +1084,14 @@ details[open] {
|
||||
background-color: var(--gray-8);
|
||||
}
|
||||
|
||||
.highlight:is(:hover, :focus-within) .rehype-twoslash-popover-target {
|
||||
background-color: var(--gray-5);
|
||||
}
|
||||
|
||||
.rehype-twoslash-popover {
|
||||
border-color: var(--gray-6);
|
||||
}
|
||||
|
||||
h6 {
|
||||
color: var(--gray-3);
|
||||
}
|
||||
@ -1062,7 +1116,6 @@ details[open] {
|
||||
}
|
||||
|
||||
pre code,
|
||||
.hljs,
|
||||
.frame-body,
|
||||
.frame-tab-item-selected,
|
||||
.frame-tab-item-dark.frame-tab-item-selected {
|
||||
@ -1231,23 +1284,6 @@ details[open] {
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix a11y. */
|
||||
.hljs-built_in,
|
||||
.hljs-symbol {
|
||||
color: hsl(24deg 92% 40%) !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.hljs-section {
|
||||
color: #488bef !important;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-symbol {
|
||||
color: #ffa657 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.playground {
|
||||
min-height: 40rem;
|
||||
gap: calc(1em + 1ex);
|
||||
|
@ -1,5 +1,7 @@
|
||||
/* eslint-disable unicorn/prefer-query-selector */
|
||||
/// <reference lib="dom" />
|
||||
|
||||
import {computePosition, shift} from '@floating-ui/dom'
|
||||
import copyToClipboard from 'copy-to-clipboard'
|
||||
import {ok as assert} from 'devlop'
|
||||
|
||||
@ -57,7 +59,35 @@ for (const copy of copies) {
|
||||
assert(copy instanceof HTMLButtonElement)
|
||||
copy.type = 'button'
|
||||
copy.replaceChildren(copyIcon.cloneNode(true))
|
||||
copy.addEventListener('click', onclick)
|
||||
copy.addEventListener('click', oncopyonclick)
|
||||
}
|
||||
|
||||
const popoverTargets = /** @type {Array<HTMLElement>} */ (
|
||||
Array.from(document.querySelectorAll('.rehype-twoslash-popover-target'))
|
||||
)
|
||||
|
||||
for (const popoverTarget of popoverTargets) {
|
||||
/** @type {NodeJS.Timeout | number} */
|
||||
let timeout = 0
|
||||
|
||||
popoverTarget.addEventListener('click', function () {
|
||||
popoverShow(popoverTarget)
|
||||
})
|
||||
|
||||
popoverTarget.addEventListener('mouseenter', function () {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(function () {
|
||||
popoverShow(popoverTarget)
|
||||
}, 300)
|
||||
})
|
||||
|
||||
popoverTarget.addEventListener('mouseleave', function () {
|
||||
clearTimeout(timeout)
|
||||
})
|
||||
|
||||
if (popoverTarget.classList.contains('rehype-twoslash-autoshow')) {
|
||||
popoverShow(popoverTarget)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,7 +96,7 @@ for (const copy of copies) {
|
||||
* @returns {undefined}
|
||||
* Nothing.
|
||||
*/
|
||||
function onclick() {
|
||||
function oncopyonclick() {
|
||||
assert(copyIcon)
|
||||
assert(copiedIcon)
|
||||
assert(this instanceof HTMLButtonElement)
|
||||
@ -84,3 +114,31 @@ function onclick() {
|
||||
this.replaceChildren(copyIcon.cloneNode(true))
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} popoverTarget
|
||||
* Popover target.
|
||||
* @returns {undefined}
|
||||
* Nothing.
|
||||
*/
|
||||
function popoverShow(popoverTarget) {
|
||||
const id = popoverTarget.dataset.popoverTarget
|
||||
if (!id) return
|
||||
const popover = document.getElementById(id)
|
||||
if (!popover) return
|
||||
|
||||
popover.showPopover()
|
||||
|
||||
computePosition(popoverTarget, popover, {
|
||||
placement: 'bottom',
|
||||
middleware: [shift({padding: 5})]
|
||||
}).then(
|
||||
/**
|
||||
* @param {{x: number, y: number}} value
|
||||
*/
|
||||
function (value) {
|
||||
popover.style.left = value.x + 'px'
|
||||
popover.style.top = value.y + 'px'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
/**
|
||||
* @import {Item} from '../_component/sort.js
|
||||
* @import {Item} from '../_component/sort.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ import {BlogGroup} from '../_component/blog.jsx'
|
||||
|
||||
export const info = {
|
||||
author: [{name: 'MDX Contributors'}],
|
||||
modified: new Date('2021-11-01'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-11-01')
|
||||
}
|
||||
export const navExcludeGroup = true
|
||||
|
@ -58,9 +58,7 @@ technically breaking.
|
||||
|
||||
We now accept block expressions right next to block JSX tags:
|
||||
|
||||
{/* Note: `language` because our theme doesn’t support it yet. */}
|
||||
|
||||
```jsx chrome=no language="mdx"
|
||||
```mdx chrome=no
|
||||
<style>{`
|
||||
|
||||
h1 {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
/**
|
||||
* @import {Item} from '../_component/sort.js
|
||||
* @import {Item} from '../_component/sort.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ import {NavigationGroup} from '../_component/nav.jsx'
|
||||
|
||||
export const info = {
|
||||
author: [{name: 'MDX Contributors'}],
|
||||
modified: new Date('2021-11-01'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-11-01')
|
||||
}
|
||||
export const navSortSelf = 6
|
||||
|
@ -4,7 +4,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-01-19'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-06')
|
||||
}
|
||||
export const navSortSelf = 4
|
||||
@ -117,7 +117,7 @@ Where to pass plugins is encoded in their name: remark plugins go in
|
||||
[`ProcessorOptions`][processor-options].
|
||||
Those fields expect lists of plugins and/or of `[plugin, options]`:
|
||||
|
||||
```tsx
|
||||
```tsx twoslash
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
import rehypeKatex from 'rehype-katex' // Render math with KaTeX.
|
||||
import remarkFrontmatter from 'remark-frontmatter' // YAML and such.
|
||||
|
@ -4,7 +4,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-24'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-05')
|
||||
}
|
||||
export const navSortSelf = 2
|
||||
@ -160,7 +160,11 @@ to highlight code blocks on GitHub is maintained at
|
||||
|
||||
…TypeScript should automatically pick it up:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.js
|
||||
// ---cut---
|
||||
import Post from './post.mdx' // `Post` is now typed.
|
||||
```
|
||||
</details>
|
||||
@ -177,7 +181,7 @@ This package also exports several useful types,
|
||||
such as `MDXComponents` which represents the `components` prop.
|
||||
You can import them like so:
|
||||
|
||||
```tsx path="example.ts"
|
||||
```ts twoslash path="example.ts"
|
||||
import type {MDXComponents} from 'mdx/types.js'
|
||||
```
|
||||
|
||||
@ -209,7 +213,7 @@ and make sure processes can be killed when taking too long.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
import mdx from '@mdx-js/esbuild'
|
||||
import esbuild from 'esbuild'
|
||||
|
||||
@ -235,16 +239,20 @@ To use more modern JavaScript features than what your users support,
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="rollup.config.js"
|
||||
```js twoslash path="rollup.config.js"
|
||||
/**
|
||||
* @import {RollupOptions} from 'rollup'
|
||||
*/
|
||||
|
||||
import mdx from '@mdx-js/rollup'
|
||||
import {babel} from '@rollup/plugin-babel'
|
||||
|
||||
/** @type {import('rollup').RollupOptions} */
|
||||
/** @type {RollupOptions} */
|
||||
const config = {
|
||||
// …
|
||||
plugins: [
|
||||
// …
|
||||
mdx({/* jsxImportSource: …, otherOptions… */})
|
||||
mdx({/* jsxImportSource: …, otherOptions… */}),
|
||||
// Babel is optional:
|
||||
babel({
|
||||
// Also run on what used to be `.mdx` (but is now JS):
|
||||
@ -275,8 +283,13 @@ for more info.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="webpack.config.js"
|
||||
/** @type {import('webpack').Configuration} */
|
||||
```js twoslash path="webpack.config.js"
|
||||
/**
|
||||
* @import {Options} from '@mdx-js/loader'
|
||||
* @import {Configuration} from 'webpack'
|
||||
*/
|
||||
|
||||
/** @type {Configuration} */
|
||||
const webpackConfig = {
|
||||
module: {
|
||||
// …
|
||||
@ -289,7 +302,7 @@ for more info.
|
||||
{loader: 'babel-loader', options: {}},
|
||||
{
|
||||
loader: '@mdx-js/loader',
|
||||
/** @type {import('@mdx-js/loader').Options} */
|
||||
/** @type {Options} */
|
||||
options: {/* jsxImportSource: …, otherOptions… */}
|
||||
}
|
||||
]
|
||||
@ -321,7 +334,7 @@ for more info.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="vite.config.js"
|
||||
```js twoslash path="vite.config.js"
|
||||
import mdx from '@mdx-js/rollup'
|
||||
import {defineConfig} from 'vite'
|
||||
|
||||
@ -344,14 +357,18 @@ To use more modern JavaScript features than what your users support,
|
||||
[configure Vite’s `build.target`][vite-build-target].
|
||||
|
||||
<Note type="info">
|
||||
**Note**: If you also use `vitejs/vite-plugin-react`,
|
||||
**Note**: If you also use `@vitejs/plugin-react`,
|
||||
you must force `@mdx-js/rollup` to run in the `pre` phase before it:
|
||||
|
||||
```tsx path="vite.config.js"
|
||||
```js twoslash path="vite.config.js"
|
||||
import mdx from '@mdx-js/rollup'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import {defineConfig} from 'vite'
|
||||
// ---cut---
|
||||
// …
|
||||
const viteConfig = defineConfig({
|
||||
plugins: [
|
||||
{enforce: 'pre', ...mdx(/* jsxImportSource: …, otherOptions… */)},
|
||||
{enforce: 'pre', ...mdx({/* jsxImportSource: …, otherOptions… */})},
|
||||
react({include: /\.(jsx|js|mdx|md|tsx|ts)$/})
|
||||
]
|
||||
})
|
||||
@ -372,49 +389,157 @@ for more info.
|
||||
|
||||
This plugin:
|
||||
|
||||
```tsx path="plugin.js"
|
||||
import path from 'node:path'
|
||||
```js twoslash path="plugin.js"
|
||||
/**
|
||||
* @import {ParseResult, ParserOptions} from '@babel/parser'
|
||||
* @import {File} from '@babel/types'
|
||||
* @import {Program} from 'estree'
|
||||
* @import {Plugin} from 'unified'
|
||||
*/
|
||||
|
||||
import parser from '@babel/parser'
|
||||
import {compileSync} from '@mdx-js/mdx'
|
||||
import estreeToBabel from 'estree-to-babel'
|
||||
|
||||
/**
|
||||
* Plugin that tells Babel to use a different parser.
|
||||
*/
|
||||
export function babelPluginSyntaxMdx() {
|
||||
// Tell Babel to use a different parser.
|
||||
return {parserOverride: babelParserWithMdx}
|
||||
}
|
||||
|
||||
// A Babel parser that parses MDX files with `@mdx-js/mdx` and passes any
|
||||
// other things through to the normal Babel parser.
|
||||
/**
|
||||
* Parser that handles MDX with `@mdx-js/mdx` and passes other things through
|
||||
* to the normal Babel parser.
|
||||
*
|
||||
* @param {string} value
|
||||
* @param {ParserOptions} options
|
||||
* @returns {ParseResult<File>}
|
||||
*/
|
||||
function babelParserWithMdx(value, options) {
|
||||
if (options.sourceFileName && /\.mdx?$/.test(options.sourceFileName)) {
|
||||
/** @type {string | undefined} */
|
||||
// @ts-expect-error: babel changed the casing at some point and the types are out of date.
|
||||
const filename = options.sourceFilename || options.sourceFileName
|
||||
|
||||
if (filename && /\.mdx?$/.test(filename)) {
|
||||
// Babel does not support async parsers, unfortunately.
|
||||
return compileSync(
|
||||
{value, path: options.sourceFileName},
|
||||
// Tell `@mdx-js/mdx` to return a Babel tree instead of serialized JS.
|
||||
{recmaPlugins: [recmaBabel], /* jsxImportSource: …, otherOptions… */}
|
||||
).result
|
||||
const file = compileSync(
|
||||
{value, path: options.sourceFilename},
|
||||
{recmaPlugins: [recmaBabel] /* jsxImportSource: …, otherOptions… */}
|
||||
)
|
||||
return /** @type {ParseResult<File>} */ (file.result)
|
||||
}
|
||||
|
||||
return parser.parse(value, options)
|
||||
}
|
||||
|
||||
// A “recma” plugin is a unified plugin that runs on the estree (used by
|
||||
// `@mdx-js/mdx` and much of the JS ecosystem but not Babel).
|
||||
// This plugin defines `'estree-to-babel'` as the compiler,
|
||||
// which means that the resulting Babel tree is given back by `compileSync`.
|
||||
/**
|
||||
* A “recma” plugin is a unified plugin that runs on the estree (used by
|
||||
* `@mdx-js/mdx` and much of the JS ecosystem but not Babel).
|
||||
* This plugin defines `'estree-to-babel'` as the compiler,
|
||||
* which means that the resulting Babel tree is given back by `compileSync`.
|
||||
*
|
||||
* @type {Plugin<[], Program, unknown>}
|
||||
*/
|
||||
function recmaBabel() {
|
||||
this.compiler = estreeToBabel
|
||||
// @ts-expect-error: `Program` is similar enough to a unist node.
|
||||
this.compiler = compiler
|
||||
|
||||
/**
|
||||
* @param {Program} tree
|
||||
* @returns {unknown}
|
||||
*/
|
||||
function compiler(tree) {
|
||||
// @ts-expect-error TS2349: This expression *is* callable, `estreeToBabel` types are wrong.
|
||||
return estreeToBabel(tree)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
…can be used like so with the Babel API:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
// @filename: plugin.js
|
||||
/**
|
||||
* @import {ParseResult, ParserOptions} from '@babel/parser'
|
||||
* @import {File} from '@babel/types'
|
||||
* @import {Program} from 'estree'
|
||||
* @import {Plugin} from 'unified'
|
||||
*/
|
||||
|
||||
import parser from '@babel/parser'
|
||||
import {compileSync} from '@mdx-js/mdx'
|
||||
import estreeToBabel from 'estree-to-babel'
|
||||
|
||||
/**
|
||||
* Plugin that tells Babel to use a different parser.
|
||||
*/
|
||||
export function babelPluginSyntaxMdx() {
|
||||
return {parserOverride: babelParserWithMdx}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser that handles MDX with `@mdx-js/mdx` and passes other things through
|
||||
* to the normal Babel parser.
|
||||
*
|
||||
* @param {string} value
|
||||
* @param {ParserOptions} options
|
||||
* @returns {ParseResult<File>}
|
||||
*/
|
||||
function babelParserWithMdx(value, options) {
|
||||
/** @type {string | undefined} */
|
||||
// @ts-expect-error: babel types are wrong.
|
||||
const filename = options.sourceFilename || options.sourceFileName
|
||||
|
||||
if (filename && /\.mdx?$/.test(filename)) {
|
||||
// Babel does not support async parsers, unfortunately.
|
||||
const file = compileSync(
|
||||
{value, path: options.sourceFilename},
|
||||
{recmaPlugins: [recmaBabel] /* jsxImportSource: …, otherOptions… */}
|
||||
)
|
||||
return /** @type {ParseResult<File>} */ (file.result)
|
||||
}
|
||||
|
||||
return parser.parse(value, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* A “recma” plugin is a unified plugin that runs on the estree (used by
|
||||
* `@mdx-js/mdx` and much of the JS ecosystem but not Babel).
|
||||
* This plugin defines `'estree-to-babel'` as the compiler,
|
||||
* which means that the resulting Babel tree is given back by `compileSync`.
|
||||
*
|
||||
* @type {Plugin<[], Program, unknown>}
|
||||
*/
|
||||
function recmaBabel() {
|
||||
// @ts-expect-error: `Program` is similar enough to a unist node.
|
||||
this.compiler = compiler
|
||||
|
||||
/**
|
||||
* @param {Program} tree
|
||||
* @returns {unknown}
|
||||
*/
|
||||
function compiler(tree) {
|
||||
// @ts-expect-error TS2349: This expression *is* callable, `estreeToBabel` types are wrong.
|
||||
return estreeToBabel(tree)
|
||||
}
|
||||
}
|
||||
// @filename: example.js
|
||||
// ---cut---
|
||||
import babel from '@babel/core'
|
||||
import {babelPluginSyntaxMdx} from './plugin.js'
|
||||
|
||||
const document = '# Hello, world!'
|
||||
|
||||
// Note that a filename must be set for our plugin to know it’s MDX instead of JS.
|
||||
await babel.transformAsync(file, {filename: 'example.mdx', plugins: [babelPluginSyntaxMdx]})
|
||||
const result = await babel.transformAsync(document, {
|
||||
filename: 'example.mdx',
|
||||
plugins: [babelPluginSyntaxMdx]
|
||||
})
|
||||
|
||||
console.log(result)
|
||||
```
|
||||
</details>
|
||||
|
||||
@ -460,7 +585,7 @@ See [`gatsby-plugin-mdx`][gatsby-plugin-mdx] on how to use MDX with Gatsby.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="next.config.js"
|
||||
```js twoslash path="next.config.js"
|
||||
import nextMdx from '@next/mdx'
|
||||
|
||||
const withMdx = nextMdx({
|
||||
@ -499,7 +624,7 @@ on how to use MDX with Parcel.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
|
||||
const js = String(await compile('# hi', {jsxImportSource: '@emotion/react', /* otherOptions… */}))
|
||||
@ -527,19 +652,27 @@ for more info.
|
||||
# Hi!
|
||||
```
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.js
|
||||
// @errors: 2769 -- something with Ink/twoslash/react getting different versions of React?
|
||||
// ---cut---
|
||||
import React from 'react'
|
||||
import {Text, render} from 'ink'
|
||||
import Content from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
const components = {
|
||||
h1(properties) {
|
||||
return React.createElement(Text, {bold: true, ...properties})
|
||||
},
|
||||
p: Text
|
||||
}
|
||||
|
||||
render(React.createElement(Content, {components}))
|
||||
render(
|
||||
React.createElement(Content, {
|
||||
components: {
|
||||
h1(properties) {
|
||||
// @ts-expect-error: `Ink` types don’t match w/ `exactOptionalPropertyTypes: true`
|
||||
return React.createElement(Text, {bold: true, ...properties})
|
||||
},
|
||||
p: Text
|
||||
}
|
||||
})
|
||||
)
|
||||
```
|
||||
|
||||
Can be used with:
|
||||
@ -563,7 +696,7 @@ info.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
|
||||
const js = String(await compile('# hi', {jsxImportSource: 'preact', /* otherOptions… */}))
|
||||
@ -593,53 +726,15 @@ for more info.
|
||||
|
||||
#### Theme UI
|
||||
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
Example w/o `@mdx-js/react`
|
||||
|
||||
```tsx path="example.js"
|
||||
import {base} from '@theme-ui/preset-base'
|
||||
import {ThemeProvider, components} from 'theme-ui'
|
||||
import Post from './post.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
<ThemeProvider theme={base}>
|
||||
<Post components={components} />
|
||||
</ThemeProvider>
|
||||
```
|
||||
|
||||
Example w/ `@mdx-js/react`
|
||||
|
||||
```tsx path="example.js"
|
||||
import {base} from '@theme-ui/preset-base'
|
||||
import {ThemeProvider} from 'theme-ui'
|
||||
import Post from './post.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
<ThemeProvider theme={base}>
|
||||
<Post />
|
||||
</ThemeProvider>
|
||||
```
|
||||
</details>
|
||||
|
||||
[Theme UI][theme-ui] is a React-specific library that depends on context to
|
||||
access its effective components.
|
||||
You can install and configure [`@mdx-js/react`][mdx-react] to support context
|
||||
based component passing.
|
||||
|
||||
See also [¶ Emotion][jsx-runtime-emotion],
|
||||
[¶ React][jsx-runtime-react],
|
||||
[¶ Rollup][bundler-rollup], and
|
||||
[¶ esbuild][bundler-esbuild],
|
||||
[¶ webpack][bundler-webpack],
|
||||
which you might be using,
|
||||
for more info.
|
||||
[Theme UI][theme-ui] has its own plugin to support MDX.
|
||||
See [`@theme-ui/mdx`][theme-ui-mdx] on how to use MDX with Theme UI.
|
||||
|
||||
#### Svelte
|
||||
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
|
||||
const js = String(await compile('# hi', {jsxImportSource: 'svelte-jsx', /* otherOptions… */}))
|
||||
@ -660,7 +755,7 @@ for more info.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
|
||||
const js = String(await compile('# hi', {jsxImportSource: 'vue', /* otherOptions… */}))
|
||||
@ -681,7 +776,7 @@ for more info.
|
||||
<details>
|
||||
<summary>Expand example</summary>
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
|
||||
const js = String(await compile('# hi', {jsxImportSource: 'solid-js/h', /* otherOptions… */}))
|
||||
@ -764,6 +859,8 @@ See its readme on how to configure it.
|
||||
|
||||
[theme-ui]: https://theme-ui.com
|
||||
|
||||
[theme-ui-mdx]: https://theme-ui.com/mdx
|
||||
|
||||
[typescript]: https://www.typescriptlang.org
|
||||
|
||||
[vim-mdx-js]: https://github.com/jxnblk/vim-mdx-js
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
/**
|
||||
* @import {Item} from '../_component/sort.js
|
||||
* @import {Item} from '../_component/sort.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ import {NavigationGroup} from '../_component/nav.jsx'
|
||||
|
||||
export const info = {
|
||||
author: [{name: 'MDX Contributors'}],
|
||||
modified: new Date('2021-11-01'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-11-01')
|
||||
}
|
||||
export const navSortSelf = 1
|
||||
|
@ -4,7 +4,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-24'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-18')
|
||||
}
|
||||
export const navSortSelf = 5
|
||||
@ -127,13 +127,13 @@ for how to create plugins.
|
||||
|
||||
The full error message in MDX 2 is as follows:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
Incorrect `format: 'detect'`: `createProcessor` can support either `md` or `mdx`; it does not support detecting the format
|
||||
```
|
||||
|
||||
The full error message in MDX 3 is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
Unexpected `format: 'detect'`, which is not supported by `createProcessor`, expected `'mdx'` or `'md'`
|
||||
```
|
||||
|
||||
@ -201,7 +201,7 @@ for examples on how to pass these values.
|
||||
|
||||
The full error message in MDX is as follows:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
Unexpected missing `options.baseUrl` needed to support `export … from`, `import`, or `import.meta.url` when generating `function-body`
|
||||
```
|
||||
|
||||
@ -240,7 +240,7 @@ It occurs when the keywords `import` or `export` are found at the start of a
|
||||
line but they are not followed by valid JavaScript.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
import 1/1
|
||||
```
|
||||
|
||||
@ -259,7 +259,7 @@ It occurs when, after an `import` or `export` statement, more JavaScript is
|
||||
found.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
export const a = 1
|
||||
const b = 2
|
||||
```
|
||||
@ -275,7 +275,7 @@ It was introduced in version 2.
|
||||
It occurs when there is an opening curly brace not followed by a closing brace.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
a { b
|
||||
```
|
||||
|
||||
@ -294,7 +294,7 @@ It was introduced in version 3.
|
||||
It occurs when containers with lazy lines are combined with expressions
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
* {1 +
|
||||
2}
|
||||
|
||||
@ -305,7 +305,7 @@ An example is:
|
||||
The reason for this error is that the parser it likely points to a bug.
|
||||
Be explicit with your list items and block quotes:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
* {1 +
|
||||
2}
|
||||
|
||||
@ -321,13 +321,13 @@ It occurs when there are matching curly braces that, when interpreting what’s
|
||||
inside them as JavaScript, results in a syntax error.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
a {const b = 'c'} d
|
||||
```
|
||||
|
||||
Another example:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
a {!} d
|
||||
```
|
||||
|
||||
@ -349,7 +349,7 @@ It occurs when there are matching curly braces that, and valid JavaScript is
|
||||
inside them, but there’s too much JavaScript.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
a {'b' 'c'} d
|
||||
```
|
||||
|
||||
@ -365,7 +365,7 @@ It was introduced in version 2.
|
||||
It occurs when there are multiple values spread into a JSX tag.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
<div {...a, ...b} />
|
||||
```
|
||||
|
||||
@ -385,7 +385,7 @@ They were introduced in version 2.
|
||||
They occur when something other than a spread is used in braces.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
<div {values} {/* comment */} {} />
|
||||
```
|
||||
|
||||
@ -404,7 +404,7 @@ They were introduced in MDX version 2.
|
||||
They occur when something unexpected was found in a JSX tag.
|
||||
Some examples are:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
<
|
||||
<.>
|
||||
</
|
||||
@ -441,7 +441,7 @@ It was introduced in version 2.
|
||||
It occurs when a closing tag is found but there are no open tags.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
</div>
|
||||
```
|
||||
|
||||
@ -455,7 +455,7 @@ It was introduced in version 3.
|
||||
It occurs when containers with lazy lines are combined with JSX.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
* <x
|
||||
y />
|
||||
|
||||
@ -466,7 +466,7 @@ y />
|
||||
The reason for this error is that the parser it likely points to a bug.
|
||||
Be explicit with your list items and block quotes:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
* <x
|
||||
y />
|
||||
|
||||
@ -481,7 +481,7 @@ It was introduced in version 2.
|
||||
It occurs when attributes are placed on closing tags.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
<h1>Text</h1 id="text">
|
||||
```
|
||||
|
||||
@ -495,7 +495,7 @@ It was introduced in version 2.
|
||||
It occurs when a closing tag is also marked as self-closing.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
<h1>Text</h1/>
|
||||
```
|
||||
|
||||
@ -511,7 +511,7 @@ It occurs when a closing tag is seen that does not match the expected opening
|
||||
tag.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
<a>Text</b>
|
||||
```
|
||||
|
||||
@ -527,7 +527,7 @@ It was introduced in version 2.
|
||||
It typically occurs when markdown and JSX are not interleaved correctly.
|
||||
An example is:
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
> <div>
|
||||
```
|
||||
|
||||
|
@ -2,7 +2,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-09-30')
|
||||
}
|
||||
export const navSortSelf = 3
|
||||
@ -40,7 +40,7 @@ export function Thing() {
|
||||
That’s *roughly* turned into the following JavaScript.
|
||||
The below might help to form a mental model:
|
||||
|
||||
```tsx path="output-outline.jsx"
|
||||
```jsx twoslash path="output-outline.jsx"
|
||||
/* @jsxRuntime automatic */
|
||||
/* @jsxImportSource react */
|
||||
|
||||
@ -62,7 +62,8 @@ Some observations:
|
||||
|
||||
The *actual* output is:
|
||||
|
||||
```tsx path="output-actual.js"
|
||||
```js twoslash path="output-actual.js"
|
||||
// @noErrors
|
||||
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
|
||||
|
||||
export function Thing() {
|
||||
@ -110,12 +111,18 @@ Take this file:
|
||||
|
||||
It could be imported and used in a React app like so:
|
||||
|
||||
```tsx path="example.js"
|
||||
import React from 'react'
|
||||
import ReactDom from 'react-dom'
|
||||
```jsx twoslash path="example.jsx"
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
// ---cut---
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
const root = ReactDom.createRoot(document.getElementById('root'))
|
||||
const container = document.getElementById('root')
|
||||
if (!container) throw new Error('Expected `root`')
|
||||
const root = createRoot(container)
|
||||
root.render(<Example />)
|
||||
```
|
||||
|
||||
@ -133,7 +140,15 @@ export function Thing() {
|
||||
|
||||
It could be imported in the following ways:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
// @filename: types.d.ts
|
||||
declare module '*.mdx' {
|
||||
export {MDXContent as default} from 'mdx/types';
|
||||
export function Thing(): JSX.Element;
|
||||
}
|
||||
// @filename: example.js
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
// A namespace import to get everything:
|
||||
import * as everything from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
console.log(everything) // {Thing: [Function: Thing], default: [Function: MDXContent]}
|
||||
@ -175,14 +190,20 @@ The current year is {props.year}
|
||||
|
||||
This file could be used as:
|
||||
|
||||
```tsx path="example.jsx"
|
||||
```jsx twoslash path="example.jsx"
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
// ---cut---
|
||||
import React from 'react'
|
||||
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
// Use a `createElement` call:
|
||||
React.createElement(Example, {name: 'Venus', year: 2021})
|
||||
console.log(React.createElement(Example, {name: 'Venus', year: 2021}))
|
||||
|
||||
// Use JSX:
|
||||
<Example name="Mars" year={2022} />
|
||||
console.log(<Example name="Mars" year={2022} />)
|
||||
```
|
||||
|
||||
### Components
|
||||
@ -197,16 +218,24 @@ Take this example:
|
||||
|
||||
It can be imported from JavaScript and passed components like so:
|
||||
|
||||
```tsx path="example.jsx"
|
||||
```jsx twoslash path="example.jsx"
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
/* @jsxImportSource react */
|
||||
// ---cut---
|
||||
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
<Example
|
||||
components={{
|
||||
Planet() {
|
||||
return <span style={{color: 'tomato'}}>Pluto</span>
|
||||
}
|
||||
}}
|
||||
/>
|
||||
console.log(
|
||||
<Example
|
||||
components={{
|
||||
Planet() {
|
||||
return <span style={{color: 'tomato'}}>Pluto</span>
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
You don’t have to pass components.
|
||||
@ -239,31 +268,40 @@ import Contributing from './docs/contributing.mdx'
|
||||
|
||||
Here are some other examples of passing components:
|
||||
|
||||
```tsx path="example.jsx"
|
||||
<Example
|
||||
components={{
|
||||
// Map `h1` (`# heading`) to use `h2`s.
|
||||
h1: 'h2',
|
||||
// Rewrite `em`s (`*like so*`) to `i` with a goldenrod foreground color.
|
||||
em(props) {
|
||||
return <i style={{color: 'goldenrod'}} {...props} />
|
||||
},
|
||||
// Pass a layout (using the special `'wrapper'` key).
|
||||
wrapper({components, ...rest}) {
|
||||
return <main {...rest} />
|
||||
},
|
||||
// Pass a component.
|
||||
Planet() {
|
||||
return 'Neptune'
|
||||
},
|
||||
// This nested component can be used as `<theme.text>hi</theme.text>`
|
||||
theme: {
|
||||
text(props) {
|
||||
return <span style={{color: 'grey'}} {...props} />
|
||||
```jsx twoslash path="example.jsx"
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
/* @jsxImportSource react */
|
||||
import Example from './example.mdx'
|
||||
// ---cut---
|
||||
console.log(
|
||||
<Example
|
||||
components={{
|
||||
// Map `h1` (`# heading`) to use `h2`s.
|
||||
h1: 'h2',
|
||||
// Rewrite `em`s (`*like so*`) to `i` with a goldenrod foreground color.
|
||||
em(props) {
|
||||
return <i style={{color: 'goldenrod'}} {...props} />
|
||||
},
|
||||
// Pass a layout (using the special `'wrapper'` key).
|
||||
wrapper({components, ...rest}) {
|
||||
return <main {...rest} />
|
||||
},
|
||||
// Pass a component.
|
||||
Planet() {
|
||||
return 'Neptune'
|
||||
},
|
||||
// This nested component can be used as `<theme.text>hi</theme.text>`
|
||||
theme: {
|
||||
text(props) {
|
||||
return <span style={{color: 'grey'}} {...props} />
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
The following keys can be passed in `components`:
|
||||
@ -276,9 +314,9 @@ The following keys can be passed in `components`:
|
||||
`<So />` or `<like.so />`, note that locally defined components take
|
||||
precedence)**‡**
|
||||
|
||||
If you ever wondered what the rules are for whether a name in JSX (so `x` in
|
||||
`<x>`) is a literal tag name (like `h1`) or not (like `Component`), they are as
|
||||
follows:
|
||||
**‡** If you ever wondered what the rules are for whether a name in JSX (so `x`
|
||||
in `<x>`) is a literal tag name (like `h1`) or not (like `Component`), they are
|
||||
as follows:
|
||||
|
||||
* If there’s a dot, it’s a member expression (`<a.b>` -> `h(a.b)`),
|
||||
which means that the `b` component is taken from object `a`
|
||||
@ -303,9 +341,7 @@ All the things.
|
||||
|
||||
The layout can also be imported and *then* exported with an `export … from`:
|
||||
|
||||
{/* Note: `language` because theme in VS Code is broken. */}
|
||||
|
||||
```tsx language="mdx"
|
||||
```mdx
|
||||
export {Layout as default} from './components.js'
|
||||
```
|
||||
|
||||
@ -325,10 +361,18 @@ Take for example this file:
|
||||
|
||||
Used like so:
|
||||
|
||||
```tsx path="app.jsx"
|
||||
```jsx twoslash path="app.jsx"
|
||||
// @filename: components.d.ts
|
||||
import React from 'react'
|
||||
import ReactDom from 'react-dom'
|
||||
import {Heading, /* … */ Table} from './components/index.js'
|
||||
declare const Heading: {H1: React.ComponentType}
|
||||
declare const Table: React.ComponentType
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
// ---cut---
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import {Heading, /* … */ Table} from './components.js'
|
||||
import Post from './post.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
const components = {
|
||||
@ -337,7 +381,9 @@ const components = {
|
||||
table: Table
|
||||
}
|
||||
|
||||
const root = ReactDom.createRoot(document.getElementById('root'))
|
||||
const container = document.getElementById('root')
|
||||
if (!container) throw new Error('Expected `root`')
|
||||
const root = createRoot(container)
|
||||
root.render(<Post components={components} />)
|
||||
```
|
||||
|
||||
@ -378,18 +424,14 @@ Set it up like so:
|
||||
|
||||
```diff
|
||||
+import {MDXProvider} from '@mdx-js/react'
|
||||
import React from 'react'
|
||||
import ReactDom from 'react-dom'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import {Heading, /* … */ Table} from './components/index.js'
|
||||
import Post from './post.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
@@ -13,4 +14,8 @@ const components = {
|
||||
|
||||
const components = {
|
||||
h1: Heading.H1,
|
||||
// …
|
||||
table: Table
|
||||
}
|
||||
|
||||
const root = ReactDom.createRoot(document.getElementById('root'))
|
||||
const container = document.getElementById('root')
|
||||
if (!container) throw new Error('Expected `root`')
|
||||
const root = createRoot(container)
|
||||
-root.render(<Post components={components} />)
|
||||
+root.render(
|
||||
+ <MDXProvider components={components}>
|
||||
@ -418,14 +460,28 @@ Now you can remove the explicit and verbose component passing:
|
||||
When `MDXProvider`s are nested, their components are merged.
|
||||
Take this example:
|
||||
|
||||
```tsx
|
||||
<>
|
||||
```jsx twoslash
|
||||
// @filename: types.d.ts
|
||||
import React from 'react'
|
||||
import type {MDXContent} from 'mdx/types.js'
|
||||
declare const Component1: React.ComponentType
|
||||
declare const Component2: React.ComponentType
|
||||
declare const Component3: React.ComponentType
|
||||
declare const Component4: React.ComponentType
|
||||
declare const Content: MDXContent
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
import {MDXProvider} from '@mdx-js/react'
|
||||
import React from 'react'
|
||||
import {Component1, Component2, Component3, Component4, Content} from './types.js'
|
||||
// ---cut---
|
||||
console.log(
|
||||
<MDXProvider components={{h1: Component1, h2: Component2}}>
|
||||
<MDXProvider components={{h2: Component3, h3: Component4}}>
|
||||
<Content />
|
||||
</MDXProvider>
|
||||
</MDXProvider>
|
||||
</>
|
||||
)
|
||||
```
|
||||
|
||||
…which results in `h1`s using `Component1`, `h2`s using `Component3`, and `h3`s
|
||||
@ -436,8 +492,22 @@ It’s given the current context `components` and what it returns will be used
|
||||
instead.
|
||||
In this example the current context components are discarded:
|
||||
|
||||
```tsx
|
||||
<>
|
||||
```jsx twoslash
|
||||
// @filename: types.d.ts
|
||||
import React from 'react'
|
||||
import type {MDXContent} from 'mdx/types.js'
|
||||
declare const Component1: React.ComponentType
|
||||
declare const Component2: React.ComponentType
|
||||
declare const Component3: React.ComponentType
|
||||
declare const Component4: React.ComponentType
|
||||
declare const Content: MDXContent
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
import {MDXProvider} from '@mdx-js/react'
|
||||
import React from 'react'
|
||||
import {Component1, Component2, Component3, Component4, Content} from './types.js'
|
||||
// ---cut---
|
||||
console.log(
|
||||
<MDXProvider components={{h1: Component1, h2: Component2}}>
|
||||
<MDXProvider
|
||||
components={
|
||||
@ -449,7 +519,7 @@ In this example the current context components are discarded:
|
||||
<Content />
|
||||
</MDXProvider>
|
||||
</MDXProvider>
|
||||
</>
|
||||
)
|
||||
```
|
||||
|
||||
…which results in `h2`s using `Component3` and `h3`s using `Component4`.
|
||||
|
@ -5,7 +5,7 @@ export const info = {
|
||||
{github: 'johno', name: 'John Otander', twitter: '4lpine'},
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-01-06'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2018-08-11')
|
||||
}
|
||||
export const navSortSelf = 1
|
||||
@ -321,7 +321,7 @@ It’s not possible to wrap “blocks” if text and tags are on the same line b
|
||||
corresponding opening and closing tags are in different blocks
|
||||
(so this is invalid!):
|
||||
|
||||
```txt chrome=no
|
||||
```mdx-invalid chrome=no
|
||||
Welcome! <a href="about.html">
|
||||
|
||||
This is home of...
|
||||
|
@ -2,7 +2,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-06')
|
||||
}
|
||||
export const navSortSelf = 5
|
||||
@ -27,7 +27,7 @@ Vue, etc.)
|
||||
You can use [`@remark-embedder/core`][remark-embedder] by doing something like
|
||||
this:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js path="example.js"
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
// Note: `@remark-embedder` is currently using faux-esm.
|
||||
import fauxRemarkEmbedder from '@remark-embedder/core'
|
||||
@ -60,7 +60,7 @@ console.log(
|
||||
<details>
|
||||
<summary>Expand equivalent JSX</summary>
|
||||
|
||||
```tsx path="output.jsx"
|
||||
```jsx path="output.jsx"
|
||||
<>
|
||||
<p>Check out this video:</p>
|
||||
<iframe
|
||||
@ -92,7 +92,7 @@ Here’s a codepen, and some other blog post text.
|
||||
<details>
|
||||
<summary>Expand equivalent JSX</summary>
|
||||
|
||||
```tsx path="output.jsx"
|
||||
```jsx path="output.jsx"
|
||||
<>
|
||||
<p>Here’s a codepen, and some other blog post text.</p>
|
||||
<CodePen codePenId="PNaGbb" />
|
||||
@ -110,23 +110,25 @@ Here’s a codepen, and some other blog post text.
|
||||
|
||||
Then you can either pass all components:
|
||||
|
||||
```tsx path="example.jsx"
|
||||
```jsx path="example.jsx"
|
||||
import * as embeds from 'mdx-embed'
|
||||
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
<Example components={...embeds} />
|
||||
console.log(<Example components={...embeds} />)
|
||||
```
|
||||
|
||||
Or, if you’ve installed and configured [`@mdx-js/react`][mdx-react], you can
|
||||
also use `MDXEmbedProvider`:
|
||||
|
||||
```tsx path="example.jsx"
|
||||
```jsx path="example.jsx"
|
||||
import {MDXEmbedProvider} from 'mdx-embed'
|
||||
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
<MDXEmbedProvider>
|
||||
<Example />
|
||||
</MDXEmbedProvider>
|
||||
console.log(
|
||||
<MDXEmbedProvider>
|
||||
<Example />
|
||||
</MDXEmbedProvider>
|
||||
)
|
||||
```
|
||||
|
||||
[commonmark]: https://spec.commonmark.org/current/
|
||||
|
@ -2,7 +2,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-06')
|
||||
}
|
||||
export const navSortSelf = 2
|
||||
@ -26,7 +26,16 @@ export const title = 'Hi, ' + name + '!'
|
||||
|
||||
Can be used like so:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
// @filename: types.d.ts
|
||||
declare module '*.mdx' {
|
||||
export {MDXContent as default} from 'mdx/types'
|
||||
export const name: string
|
||||
export const title: string
|
||||
}
|
||||
// @filename: example.js
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
import * as Post from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
console.log(Post.title) // Prints 'Hi, World!'
|
||||
@ -46,7 +55,9 @@ title: Hi, World!
|
||||
|
||||
Then without compiling or evaluating the metadata can be accessed like so:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
import {read} from 'to-vfile'
|
||||
import {matter} from 'vfile-matter'
|
||||
|
||||
@ -60,7 +71,10 @@ Our compiler, `@mdx-js/mdx`, doesn’t understand YAML frontmatter by default bu
|
||||
it can be enabled by using a remark plugin,
|
||||
[`remark-frontmatter`][remark-frontmatter]:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
// @filename: example.js
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
import fs from 'node:fs/promises'
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
import remarkFrontmatter from 'remark-frontmatter'
|
||||
|
@ -2,7 +2,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-06')
|
||||
}
|
||||
export const navSortSelf = 1
|
||||
@ -51,7 +51,10 @@ A note[^1]
|
||||
|
||||
The above MDX with GFM can be transformed with the following module:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
// @filename: example.js
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
import fs from 'node:fs/promises'
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
@ -66,7 +69,7 @@ console.log(
|
||||
<details>
|
||||
<summary>Expand equivalent JSX</summary>
|
||||
|
||||
```tsx path="output.jsx"
|
||||
```jsx path="output.jsx"
|
||||
<>
|
||||
<h1>GFM</h1>
|
||||
<h2>Autolink literals</h2>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
/**
|
||||
* @import {Item} from '../_component/sort.js
|
||||
* @import {Item} from '../_component/sort.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ import {NavigationGroup} from '../_component/nav.jsx'
|
||||
|
||||
export const info = {
|
||||
author: [{name: 'MDX Contributors'}],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-11-01')
|
||||
}
|
||||
export const navSortSelf = 2
|
||||
|
@ -2,7 +2,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-24'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2023-10-24')
|
||||
}
|
||||
export const navSortSelf = 7
|
||||
@ -25,19 +25,27 @@ as you can pass components to MDX:
|
||||
|
||||
You can pass `Planet` and say a component used instead of the `h1`:
|
||||
|
||||
```tsx path="example.jsx"
|
||||
```jsx twoslash path="example.jsx"
|
||||
// @filename: types.d.ts
|
||||
import type {} from 'mdx'
|
||||
// @filename: example.jsx
|
||||
/// <reference lib="dom" />
|
||||
/* @jsxImportSource react */
|
||||
// ---cut---
|
||||
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
<Example
|
||||
components={{
|
||||
Planet() {
|
||||
return 'Pluto'
|
||||
},
|
||||
h1(properties) {
|
||||
return <h2 {...properties} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
console.log(
|
||||
<Example
|
||||
components={{
|
||||
Planet() {
|
||||
return 'Pluto'
|
||||
},
|
||||
h1(properties) {
|
||||
return <h2 {...properties} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
When you find yourself passing that `components` prop around a lot,
|
||||
@ -91,8 +99,15 @@ We can use this interface to inject components from a file.
|
||||
In that file,
|
||||
we need a `useMDXComponents` function that returns our components.
|
||||
|
||||
```tsx path="mdx-components.js"
|
||||
/** @returns {import('mdx/types.js').MDXComponents} */
|
||||
```jsx twoslash path="mdx-components.js"
|
||||
// @filename: mdx-components.jsx
|
||||
/* @jsxImportSource react */
|
||||
// ---cut---
|
||||
/**
|
||||
* @import {MDXComponents} from 'mdx/types.js'
|
||||
*/
|
||||
|
||||
/** @returns {MDXComponents} */
|
||||
export function useMDXComponents() {
|
||||
return {
|
||||
Planet() {
|
||||
|
@ -4,7 +4,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-06')
|
||||
}
|
||||
export const navSortSelf = 3
|
||||
@ -30,7 +30,10 @@ Say we have an MDX file like this:
|
||||
|
||||
The above MDX with math can be transformed with the following module:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
// @filename: example.js
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
import fs from 'node:fs/promises'
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
@ -49,7 +52,7 @@ console.log(
|
||||
<details>
|
||||
<summary>Expand equivalent JSX</summary>
|
||||
|
||||
```tsx path="output.jsx"
|
||||
```jsx path="output.jsx"
|
||||
<>
|
||||
<h1>
|
||||
<span className="katex">
|
||||
|
@ -4,7 +4,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-11-13')
|
||||
}
|
||||
export const navSortSelf = 6
|
||||
@ -26,7 +26,7 @@ This is similar to what people sometimes use [`mdx-bundler`][mdx-bundler] or
|
||||
|
||||
On the server:
|
||||
|
||||
```tsx path="server.js"
|
||||
```js twoslash path="server.js"
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
|
||||
const code = String(await compile('# hi', {
|
||||
@ -38,12 +38,13 @@ const code = String(await compile('# hi', {
|
||||
|
||||
On the client:
|
||||
|
||||
```tsx path="client.js"
|
||||
```js twoslash path="client.js"
|
||||
import {run} from '@mdx-js/mdx'
|
||||
import * as runtime from 'react/jsx-runtime'
|
||||
|
||||
const code = '' // To do: get `code` from server somehow.
|
||||
|
||||
// @ts-expect-error: `runtime` types are currently broken.
|
||||
const {default: Content} = await run(code, {...runtime, baseUrl: import.meta.url})
|
||||
```
|
||||
|
||||
@ -66,20 +67,34 @@ and runs in one.
|
||||
Some frameworks let you write the server and client code in one file, such as
|
||||
Next.
|
||||
|
||||
```tsx path="pages/hello.js"
|
||||
```js twoslash path="pages/hello.js"
|
||||
/**
|
||||
* @import {MDXModule} from 'mdx/types.js'
|
||||
* @import {Dispatch, SetStateAction} from 'react'
|
||||
*/
|
||||
|
||||
import {compile, run} from '@mdx-js/mdx'
|
||||
import {Fragment, useEffect, useState} from 'react'
|
||||
import * as runtime from 'react/jsx-runtime'
|
||||
|
||||
/**
|
||||
* @param {{code: string}} props
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export default function Page({code}) {
|
||||
/** @type {[MDXModule | undefined, Dispatch<SetStateAction<MDXModule | undefined>>]} */
|
||||
const [mdxModule, setMdxModule] = useState()
|
||||
const Content = mdxModule ? mdxModule.default : Fragment
|
||||
|
||||
useEffect(function () {
|
||||
;(async function () {
|
||||
setMdxModule(await run(code, {...runtime, baseUrl: import.meta.url}))
|
||||
})()
|
||||
}, [code])
|
||||
useEffect(
|
||||
function () {
|
||||
;(async function () {
|
||||
// @ts-expect-error: `runtime` types are currently broken.
|
||||
setMdxModule(await run(code, {...runtime, baseUrl: import.meta.url}))
|
||||
})()
|
||||
},
|
||||
[code]
|
||||
)
|
||||
|
||||
return <Content />
|
||||
}
|
||||
@ -87,7 +102,7 @@ export default function Page({code}) {
|
||||
export async function getStaticProps() {
|
||||
const code = String(
|
||||
await compile('# hi', {
|
||||
outputFormat: 'function-body',
|
||||
outputFormat: 'function-body'
|
||||
/* …otherOptions */
|
||||
})
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ export const info = {
|
||||
author: [
|
||||
{github: 'wooorm', name: 'Titus Wormer', twitter: 'wooorm'}
|
||||
],
|
||||
modified: new Date('2023-10-23'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-10-06')
|
||||
}
|
||||
export const navSortSelf = 4
|
||||
@ -28,33 +28,34 @@ Vue, etc.)
|
||||
|
||||
## Syntax highlighting at compile time
|
||||
|
||||
Use either [`rehype-highlight`][rehype-highlight]
|
||||
(`highlight.js`) or [`@mapbox/rehype-prism`][rehype-prism]
|
||||
(Prism) by doing something like this:
|
||||
Use for example [`rehype-starry-night`][rehype-starry-night] (`starry-night`),
|
||||
[`rehype-highlight`][rehype-highlight] (`lowlight`, `highlight.js`),
|
||||
or [`@mapbox/rehype-prism`][rehype-prism] (`refractor`, `prism`)
|
||||
by doing something like this:
|
||||
|
||||
```tsx path="example.js"
|
||||
```js twoslash path="example.js"
|
||||
/// <reference types="node" />
|
||||
// ---cut---
|
||||
import {compile} from '@mdx-js/mdx'
|
||||
import rehypeHighlight from 'rehype-highlight'
|
||||
import rehypeStarryNight from 'rehype-starry-night'
|
||||
|
||||
const code = `~~~js
|
||||
console.log(1)
|
||||
~~~`
|
||||
|
||||
console.log(
|
||||
String(await compile(code, {rehypePlugins: [rehypeHighlight]}))
|
||||
String(await compile(code, {rehypePlugins: [rehypeStarryNight]}))
|
||||
)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Expand equivalent JSX</summary>
|
||||
|
||||
```tsx path="output.jsx"
|
||||
```jsx path="output.jsx"
|
||||
<>
|
||||
<pre>
|
||||
<code className="hljs language-js">
|
||||
<span className="hljs-variable language_">console</span>.
|
||||
<span className="hljs-title function_">log</span>(
|
||||
<span className="hljs-number">1</span>)
|
||||
<code className="language-js">
|
||||
<span className="pl-en">console</span>.<span className="pl-c1">log</span>(<span className="pl-c1">1</span>)
|
||||
</code>
|
||||
</pre>
|
||||
</>
|
||||
@ -62,24 +63,8 @@ console.log(
|
||||
</details>
|
||||
|
||||
<Note type="important">
|
||||
**Important**: If you chose `rehype-highlight`, then you should also use a
|
||||
highlight.js theme somewhere on the page.
|
||||
For example, to get GitHub Dark from cdnjs:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css">
|
||||
```
|
||||
|
||||
{/* to do: once in a while, get the latest: <https://github.com/wooorm/lowlight#css> */}
|
||||
|
||||
If you chose `@mapbox/rehype-prism`, include something like this instead to
|
||||
get Prism Dark:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/themes/prism-dark.min.css">
|
||||
```
|
||||
|
||||
{/* to do: once in a while, get the latest: <https://github.com/wooorm/refractor#css> */}
|
||||
**Important**: you must likely also include CSS somewhere on the page.
|
||||
See the documentation of the plugin you’re using for more information.
|
||||
</Note>
|
||||
|
||||
## Syntax highlighting at run time
|
||||
@ -88,11 +73,13 @@ Use for example
|
||||
[`react-syntax-highlighter`][react-syntax-highlighter],
|
||||
by doing something like this:
|
||||
|
||||
```tsx path="example.jsx"
|
||||
{/* Note: `react-syntax-highlighter` doesn’t seem actively maintained so no twoslash to check this example. */}
|
||||
|
||||
```jsx path="example.jsx"
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter'
|
||||
import Post from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
|
||||
|
||||
<Post components={{code}} />
|
||||
console.log(<Post components={{code}} />)
|
||||
|
||||
function code({className, ...properties}) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
@ -105,7 +92,7 @@ function code({className, ...properties}) {
|
||||
<details>
|
||||
<summary>Expand equivalent JSX</summary>
|
||||
|
||||
```tsx path="output.jsx"
|
||||
```jsx path="output.jsx"
|
||||
<>
|
||||
<pre>
|
||||
<div
|
||||
@ -161,6 +148,8 @@ More info on plugins is available in [§ Extending MDX][extend]
|
||||
|
||||
[commonmark]: https://spec.commonmark.org/current/
|
||||
|
||||
[rehype-starry-night]: https://github.com/rehypejs/rehype-starry-night
|
||||
|
||||
[rehype-highlight]: https://github.com/rehypejs/rehype-highlight
|
||||
|
||||
[rehype-prism]: https://github.com/mapbox/rehype-prism
|
||||
|
@ -590,7 +590,7 @@ You can more easily embed components in MDX because blank lines are allowed:
|
||||
|
||||
{/* Note: `language` because theme in VS Code is broken. */}
|
||||
|
||||
```tsx language="mdx" chrome=no
|
||||
```mdx chrome=no
|
||||
export function Button(props) {
|
||||
const style = {color: 'red'}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
/**
|
||||
* @import {Item} from '../_component/sort.js
|
||||
* @import {Item} from '../_component/sort.js'
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ import {NavigationGroup} from '../_component/nav.jsx'
|
||||
|
||||
export const info = {
|
||||
author: [{name: 'MDX Contributors'}],
|
||||
modified: new Date('2021-11-01'),
|
||||
modified: new Date('2024-07-04'),
|
||||
published: new Date('2021-11-01')
|
||||
}
|
||||
export const navSortSelf = 3
|
||||
|
1992
package-lock.json
generated
1992
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -21,16 +21,22 @@
|
||||
"packages/node-loader/",
|
||||
"packages/rollup/"
|
||||
],
|
||||
"#": "note: `lz-string` is included because `@typescript/vfs` (through `twoslash`) types use it w/o marking it as a dep",
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.0.0",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"@next/mdx": "^14.0.0",
|
||||
"@node-loader/core": "^2.0.0",
|
||||
"@rollup/plugin-babel": "^6.0.0",
|
||||
"@sparticuz/chromium": "^123.0.0",
|
||||
"@types/babel__core": "^7.0.0",
|
||||
"@types/dlv": "^1.0.0",
|
||||
"@types/mdx": "^2.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/ungap__structured-clone": "^1.0.0",
|
||||
"@ungap/structured-clone": "^1.0.0",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"@vue/server-renderer": "^3.0.0",
|
||||
"@wooorm/starry-night": "^3.0.0",
|
||||
"acorn": "^8.0.0",
|
||||
@ -47,6 +53,7 @@
|
||||
"eslint-plugin-es": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.0.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.0",
|
||||
"estree-to-babel": "^9.0.0",
|
||||
"estree-util-value-to-estree": "^3.0.0",
|
||||
"globby": "^14.0.0",
|
||||
"hast-util-from-html": "^2.0.0",
|
||||
@ -56,6 +63,8 @@
|
||||
"hast-util-to-jsx-runtime": "^2.0.0",
|
||||
"hast-util-to-text": "^4.0.0",
|
||||
"hastscript": "^9.0.0",
|
||||
"ink": "^5.0.0",
|
||||
"lz-string": "^1.0.0",
|
||||
"p-all": "^5.0.0",
|
||||
"periscopic": "^3.0.0",
|
||||
"postcss": "^8.0.0",
|
||||
@ -83,6 +92,7 @@
|
||||
"rehype-slug": "^6.0.0",
|
||||
"rehype-starry-night": "^2.0.0",
|
||||
"rehype-stringify": "^10.0.0",
|
||||
"rehype-twoslash": "^1.0.0",
|
||||
"remark-cli": "^12.0.0",
|
||||
"remark-directive": "^3.0.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
@ -104,6 +114,7 @@
|
||||
"unist-util-remove-position": "^5.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vfile": "^6.0.0",
|
||||
"vfile-matter": "^5.0.0",
|
||||
"vfile-message": "^4.0.0",
|
||||
"vite": "^5.0.0",
|
||||
"vue": "^3.0.0",
|
||||
|
@ -57,7 +57,12 @@ npm install @mdx-js/loader
|
||||
Add something along these lines to your `webpack.config.js`:
|
||||
|
||||
```tsx
|
||||
/** @type {import('webpack').Configuration} */
|
||||
/**
|
||||
* @import {Options} from '@mdx-js/loader'
|
||||
* @import {Configuration} from 'webpack'
|
||||
*/
|
||||
|
||||
/** @type {Configuration} */
|
||||
const webpackConfig = {
|
||||
module: {
|
||||
// …
|
||||
@ -68,7 +73,7 @@ const webpackConfig = {
|
||||
use: [
|
||||
{
|
||||
loader: '@mdx-js/loader',
|
||||
/** @type {import('@mdx-js/loader').Options} */
|
||||
/** @type {Options} */
|
||||
options: {/* jsxImportSource: …, otherOptions… */}
|
||||
}
|
||||
]
|
||||
@ -111,7 +116,12 @@ If you use modern JavaScript features you might want to use Babel through
|
||||
[`babel-loader`][babel-loader] to compile to code that works in older browsers:
|
||||
|
||||
```tsx
|
||||
/** @type {import('webpack').Configuration} */
|
||||
/**
|
||||
* @import {Options} from '@mdx-js/loader'
|
||||
* @import {Configuration} from 'webpack'
|
||||
*/
|
||||
|
||||
/** @type {Configuration} */
|
||||
const webpackConfig = {
|
||||
module: {
|
||||
// …
|
||||
@ -125,7 +135,7 @@ const webpackConfig = {
|
||||
{loader: 'babel-loader', options: {}},
|
||||
{
|
||||
loader: '@mdx-js/loader',
|
||||
/** @type {import('@mdx-js/loader').Options} */
|
||||
/** @type {Options} */
|
||||
options: {}
|
||||
}
|
||||
]
|
||||
|
@ -66,6 +66,10 @@ In browsers with [`esm.sh`][esmsh]:
|
||||
## Use
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* @import {MDXComponents} from 'mdx/types.js'
|
||||
*/
|
||||
|
||||
import {MDXProvider} from '@mdx-js/preact'
|
||||
import Post from './post.mdx'
|
||||
// ^-- Assumes an integration is used to compile MDX to JS, such as
|
||||
@ -73,7 +77,7 @@ 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} */
|
||||
/** @type {MDXComponents} */
|
||||
const components = {
|
||||
em(properties) {
|
||||
return <i {...properties} />
|
||||
|
@ -70,6 +70,10 @@ In browsers with [`esm.sh`][esmsh]:
|
||||
## Use
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* @import {MDXComponents} from 'mdx/types.js'
|
||||
*/
|
||||
|
||||
import {MDXProvider} from '@mdx-js/react'
|
||||
import Post from './post.mdx'
|
||||
// ^-- Assumes an integration is used to compile MDX to JS, such as
|
||||
@ -77,7 +81,7 @@ 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} */
|
||||
/** @type {MDXComponents} */
|
||||
const components = {
|
||||
em(properties) {
|
||||
return <i {...properties} />
|
||||
|
@ -57,9 +57,13 @@ npm install @mdx-js/rollup
|
||||
Add something along these lines to your `rollup.config.js`:
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* @import {RollupOptions} from 'rollup'
|
||||
*/
|
||||
|
||||
import mdx from '@mdx-js/rollup'
|
||||
|
||||
/** @type {import('rollup').RollupOptions} */
|
||||
/** @type {RollupOptions} */
|
||||
const config = {
|
||||
// …
|
||||
plugins: [
|
||||
@ -119,10 +123,14 @@ If you use modern JavaScript features you might want to use Babel through
|
||||
[`@rollup/plugin-babel`][rollup-plugin-babel] to compile to code that works:
|
||||
|
||||
```tsx
|
||||
/**
|
||||
* @import {RollupOptions} from 'rollup'
|
||||
*/
|
||||
|
||||
import mdx from '@mdx-js/rollup'
|
||||
import {babel} from '@rollup/plugin-babel'
|
||||
|
||||
/** @type {import('rollup').RollupOptions} */
|
||||
/** @type {RollupOptions} */
|
||||
const config = {
|
||||
// …
|
||||
plugins: [
|
||||
|
@ -9,6 +9,7 @@
|
||||
"jsx": "preserve",
|
||||
"lib": ["es2022"],
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16",
|
||||
"strict": true,
|
||||
"target": "es2022"
|
||||
},
|
||||
|
@ -14,7 +14,8 @@
|
||||
* List of keys to exclude (optional).
|
||||
*/
|
||||
|
||||
import {pathToFileURL} from 'node:url'
|
||||
import assert from 'node:assert/strict'
|
||||
import {fileURLToPath, pathToFileURL} from 'node:url'
|
||||
import {nodeTypes} from '@mdx-js/mdx'
|
||||
import {common} from '@wooorm/starry-night'
|
||||
import sourceMdx from '@wooorm/starry-night/source.mdx'
|
||||
@ -28,11 +29,12 @@ import rehypeInferDescriptionMeta from 'rehype-infer-description-meta'
|
||||
import rehypeInferReadingTimeMeta from 'rehype-infer-reading-time-meta'
|
||||
import rehypeInferTitleMeta from 'rehype-infer-title-meta'
|
||||
import rehypeMinifyUrl from 'rehype-minify-url'
|
||||
import rehypePresetMinify from 'rehype-preset-minify'
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
import rehypeShiftHeading from 'rehype-shift-heading'
|
||||
import rehypeSlug from 'rehype-slug'
|
||||
import rehypeStarryNight from 'rehype-starry-night'
|
||||
import rehypePresetMinify from 'rehype-preset-minify'
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
import rehypeTwoslash from 'rehype-twoslash'
|
||||
import remarkFrontmatter from 'remark-frontmatter'
|
||||
import remarkGemoji from 'remark-gemoji'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
@ -42,8 +44,31 @@ import remarkSqueezeParagraphs from 'remark-squeeze-paragraphs'
|
||||
import remarkStripBadges from 'remark-strip-badges'
|
||||
import remarkToc from 'remark-toc'
|
||||
import {visit} from 'unist-util-visit'
|
||||
import typescript from 'typescript'
|
||||
import {config} from '../docs/_config.js'
|
||||
|
||||
const configPath = typescript.findConfigFile(
|
||||
fileURLToPath(import.meta.url),
|
||||
typescript.sys.fileExists,
|
||||
'tsconfig.json'
|
||||
)
|
||||
assert(configPath)
|
||||
const commandLine = typescript.getParsedCommandLineOfConfigFile(
|
||||
configPath,
|
||||
undefined,
|
||||
{
|
||||
fileExists: typescript.sys.fileExists,
|
||||
getCurrentDirectory: typescript.sys.getCurrentDirectory,
|
||||
onUnRecoverableConfigFileDiagnostic(x) {
|
||||
console.warn('Unrecoverable diagnostic', x)
|
||||
},
|
||||
readDirectory: typescript.sys.readDirectory,
|
||||
readFile: typescript.sys.readFile,
|
||||
useCaseSensitiveFileNames: typescript.sys.useCaseSensitiveFileNames
|
||||
}
|
||||
)
|
||||
assert(commandLine)
|
||||
|
||||
/** @type {Readonly<CompileOptions>} */
|
||||
const options = {
|
||||
recmaPlugins: [recmaInjectMeta],
|
||||
@ -67,7 +92,14 @@ const options = {
|
||||
properties: {ariaLabel: 'Link to this section', className: ['anchor']}
|
||||
}
|
||||
],
|
||||
[rehypeStarryNight, {grammars: [...common, sourceMdx, sourceTsx]}],
|
||||
[
|
||||
rehypeStarryNight,
|
||||
{
|
||||
grammars: [...common, sourceMdx, sourceTsx],
|
||||
plainText: ['mdx-invalid', 'txt']
|
||||
}
|
||||
],
|
||||
[rehypeTwoslash, {twoslash: {compilerOptions: commandLine.options}}],
|
||||
rehypePresetMinify,
|
||||
rehypeMinifyUrl
|
||||
],
|
||||
@ -258,6 +290,14 @@ function rehypePrettyCodeBlocks() {
|
||||
}
|
||||
}
|
||||
|
||||
const className = Array.isArray(code.properties.className)
|
||||
? code.properties.className
|
||||
: (code.properties.className = [])
|
||||
|
||||
if (metaProperties.twoslash === '') {
|
||||
className.push('twoslash')
|
||||
}
|
||||
|
||||
if (metaProperties.chrome === 'no') {
|
||||
return
|
||||
}
|
||||
@ -265,9 +305,6 @@ function rehypePrettyCodeBlocks() {
|
||||
const textContent = toText(node)
|
||||
/** @type {Array<ElementContent>} */
|
||||
const children = [node]
|
||||
const className = Array.isArray(code.properties.className)
|
||||
? code.properties.className
|
||||
: []
|
||||
const lang = className.find(function (value) {
|
||||
return String(value).slice(0, 9) === 'language-'
|
||||
})
|
||||
@ -275,8 +312,7 @@ function rehypePrettyCodeBlocks() {
|
||||
const footer = []
|
||||
/** @type {Array<ElementContent>} */
|
||||
const header = []
|
||||
const language =
|
||||
metaProperties.language || (lang ? String(lang).slice(9) : undefined)
|
||||
const language = lang ? String(lang).slice(9) : undefined
|
||||
|
||||
// Not giant.
|
||||
if (textContent.length < 8192 && metaProperties.copy !== 'no') {
|
||||
|
Loading…
Reference in New Issue
Block a user