├── .editorconfig ├── .github └── workflows │ ├── bb.yml │ ├── main.yml │ └── website.yml ├── .gitignore ├── .prettierignore ├── .vercelignore ├── changelog.md ├── docs ├── .remarkrc.js ├── 404.mdx ├── _asset │ ├── editor.jsx │ ├── index.css │ └── index.js ├── _component │ ├── blog.jsx │ ├── foot-site.jsx │ ├── home.jsx │ ├── icon │ │ ├── github.jsx │ │ ├── mdx.jsx │ │ └── open-collective.jsx │ ├── layout.jsx │ ├── nav-site.jsx │ ├── nav.jsx │ ├── note.jsx │ ├── snowfall.jsx │ └── sort.js ├── _config.js ├── _static │ ├── favicon.ico │ ├── icon.svg │ ├── og-v2.png │ └── og.png ├── blog │ ├── conf.mdx │ ├── custom-pragma.mdx │ ├── index.mdx │ ├── shortcodes.mdx │ ├── v1.mdx │ ├── v2.mdx │ └── v3.mdx ├── community │ ├── about.mdx │ ├── contribute.mdx │ ├── index.mdx │ ├── projects.mdx │ ├── sponsor.mdx │ └── support.mdx ├── docs │ ├── extending-mdx.mdx │ ├── getting-started.mdx │ ├── index.mdx │ ├── troubleshooting-mdx.mdx │ ├── using-mdx.mdx │ └── what-is-mdx.mdx ├── guides │ ├── embed.mdx │ ├── frontmatter.mdx │ ├── gfm.mdx │ ├── index.mdx │ ├── injecting-components.mdx │ ├── math.mdx │ ├── mdx-on-demand.mdx │ └── syntax-highlighting.mdx ├── index.mdx ├── migrating │ ├── v1.mdx │ ├── v2.mdx │ └── v3.mdx ├── packages │ ├── esbuild.md │ ├── index.mdx │ ├── loader.md │ ├── mdx.md │ ├── node-loader.md │ ├── preact.md │ ├── react.md │ ├── remark-mdx.md │ ├── rollup.md │ └── vue.md ├── playground.mdx └── table-of-components.mdx ├── license ├── package-lock.json ├── package.json ├── packages ├── esbuild │ ├── index.js │ ├── lib │ │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ └── index.js │ └── tsconfig.json ├── loader │ ├── index.cjs │ ├── lib │ │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ └── index.js │ └── tsconfig.json ├── mdx │ ├── index.js │ ├── lib │ │ ├── compile.js │ │ ├── core.js │ │ ├── evaluate.js │ │ ├── node-types.js │ │ ├── plugin │ │ │ ├── recma-build-jsx-transform.js │ │ │ ├── recma-document.js │ │ │ ├── recma-jsx-rewrite.js │ │ │ ├── rehype-remove-raw.js │ │ │ └── remark-mark-and-unravel.js │ │ ├── run.js │ │ ├── types.d.ts │ │ └── util │ │ │ ├── create-format-aware-processors.js │ │ │ ├── estree-util-create.js │ │ │ ├── estree-util-declaration-to-expression.js │ │ │ ├── estree-util-is-declaration.js │ │ │ ├── estree-util-specifiers-to-declarations.js │ │ │ ├── estree-util-to-binary-addition.js │ │ │ ├── estree-util-to-id-or-member-expression.js │ │ │ ├── extnames-to-regex.js │ │ │ ├── extnames.js │ │ │ ├── resolve-evaluate-options.js │ │ │ └── resolve-file-and-options.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ ├── compile.js │ │ ├── context │ │ │ ├── components.js │ │ │ ├── data.js │ │ │ └── run.js │ │ ├── core.js │ │ ├── evaluate.js │ │ ├── index.js │ │ └── syntax.js │ └── tsconfig.json ├── node-loader │ ├── index.js │ ├── lib │ │ ├── condition.default.js │ │ ├── condition.development.js │ │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ └── index.js │ └── tsconfig.json ├── preact │ ├── index.js │ ├── lib │ │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ └── index.jsx │ └── tsconfig.json ├── react │ ├── index.js │ ├── lib │ │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ └── index.jsx │ └── tsconfig.json ├── remark-mdx │ ├── index.js │ ├── lib │ │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ └── index.js │ └── tsconfig.json ├── rollup │ ├── index.js │ ├── lib │ │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ │ ├── index.js │ │ └── vite-entry.mdx │ └── tsconfig.json └── vue │ ├── index.js │ ├── lib │ └── index.js │ ├── license │ ├── package.json │ ├── readme.md │ ├── test │ └── index.js │ └── tsconfig.json ├── readme.md ├── renovate.json5 ├── script └── jsx-loader.js ├── tsconfig.json ├── vercel.json └── website ├── generate.js ├── loader.js ├── mdx-loader.js ├── post.js ├── prep.js ├── schema-description.js └── types.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.github/workflows/bb.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | main: 3 | runs-on: ubuntu-latest 4 | steps: 5 | - uses: unifiedjs/beep-boop-beta@main 6 | with: 7 | repo-token: ${{secrets.GITHUB_TOKEN}} 8 | name: bb 9 | on: 10 | issues: 11 | types: [closed, edited, labeled, opened, reopened, unlabeled] 12 | pull_request_target: 13 | types: [closed, edited, labeled, opened, reopened, unlabeled] 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | small: 3 | env: 4 | PUPPETEER_SKIP_DOWNLOAD: 1 5 | name: test / ${{matrix.os}} / ${{matrix.node}} 6 | runs-on: ${{matrix.os}} 7 | steps: 8 | - uses: actions/checkout@v4 9 | with: 10 | fetch-depth: 0 11 | - uses: actions/setup-node@v4 12 | with: 13 | cache: npm 14 | node-version: ${{matrix.node}} 15 | - run: npm ci 16 | - run: npm run test-api 17 | strategy: 18 | matrix: 19 | include: 20 | - node: lts/hydrogen 21 | os: ubuntu-latest 22 | node: 23 | - node 24 | os: 25 | - macos-latest 26 | - windows-latest 27 | full: 28 | env: 29 | PUPPETEER_SKIP_DOWNLOAD: 1 30 | name: full build 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4 34 | with: 35 | fetch-depth: 0 36 | - uses: actions/setup-node@v4 37 | with: 38 | cache: npm 39 | node-version: node 40 | - run: npm ci 41 | - run: npm test 42 | - uses: codecov/codecov-action@v5 43 | name: main 44 | on: 45 | - pull_request 46 | - push 47 | -------------------------------------------------------------------------------- /.github/workflows/website.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | deploy: 3 | environment: 4 | name: Website 5 | url: ${{steps.deployment.outputs.page_url}} 6 | permissions: 7 | contents: read 8 | id-token: write 9 | pages: write 10 | # To do: replace w/ `ubuntu-latest` when upstream is solved: 11 | # . 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: node 18 | - run: npm ci 19 | - run: npm run docs 20 | - uses: actions/upload-pages-artifact@v3 21 | with: 22 | path: public 23 | - uses: actions/deploy-pages@v4 24 | id: deployment 25 | name: website 26 | on: 27 | push: 28 | branches: 29 | - main 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | coverage/ 3 | node_modules/ 4 | *.d.cts 5 | *.d.ts 6 | *.map 7 | *.tsbuildinfo 8 | /public/ 9 | !/packages/mdx/lib/types.d.ts 10 | !/website/types.d.ts 11 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | public/ 4 | *.md 5 | *.mdx 6 | -------------------------------------------------------------------------------- /.vercelignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | See [GitHub Releases][releases] for the changelog. 4 | 5 | [releases]: https://github.com/mdx-js/mdx/releases 6 | -------------------------------------------------------------------------------- /docs/.remarkrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {CheckFlag} from 'remark-lint-fenced-code-flag' 3 | * @import {Preset} from 'unified' 4 | */ 5 | 6 | import remarkPresetWooorm from 'remark-preset-wooorm' 7 | import remarkLintFencedCodeFlag, { 8 | checkGithubLinguistFlag 9 | } from 'remark-lint-fenced-code-flag' 10 | import remarkLintNoHtml from 'remark-lint-no-html' 11 | import remarkValidateLinks from 'remark-validate-links' 12 | 13 | /** @type {Preset} */ 14 | const remarkPresetMdx = { 15 | plugins: [ 16 | remarkPresetWooorm, 17 | [remarkLintFencedCodeFlag, check], 18 | [remarkLintNoHtml, false], 19 | [remarkValidateLinks, false] 20 | ] 21 | } 22 | 23 | export default remarkPresetMdx 24 | 25 | /** 26 | * Check according to GitHub Linguist. 27 | * 28 | * @param {string} value 29 | * Language flag to check. 30 | * @returns {string | undefined} 31 | * Whether the flag is valid (`undefined`), 32 | * or a message to warn about (`string`). 33 | * @satisfies {CheckFlag} 34 | */ 35 | function check(value) { 36 | // To do: investigate if we can change ` ```jsx ` -> ` ```js `? 37 | if (value === 'jsx' || value === 'mdx-invalid') return undefined 38 | return checkGithubLinguistFlag(value) 39 | } 40 | -------------------------------------------------------------------------------- /docs/404.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from './_component/note.jsx' 2 | 3 | export {Home as default} from './_component/home.jsx' 4 | export const navExclude = true 5 | 6 | # 404: Not found 7 | 8 | Aww, snap. 9 | Unfortunately this page doesn’t exist. 10 | Perhaps you can find what you’re looking for [on GitHub][search]? 11 | 12 | 13 | **Note**: Did you come here from a website linking to it? 14 | Pretty sure this page used to exist? 15 | Please [open an issue](https://github.com/mdx-js/mdx/issues/new) to let us 16 | know so we can fix it! 17 | 18 | 19 | [search]: https://github.com/mdx-js/mdx/search 20 | -------------------------------------------------------------------------------- /docs/_asset/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/prefer-query-selector */ 2 | /// 3 | 4 | import docsearch_ from '@docsearch/js' 5 | import {computePosition, shift} from '@floating-ui/dom' 6 | import copyToClipboard from 'copy-to-clipboard' 7 | import {ok as assert} from 'devlop' 8 | 9 | // Squircles. 10 | if ('paintWorklet' in CSS) { 11 | // @ts-expect-error: TS doesn’t understand Houdini. 12 | CSS.paintWorklet.addModule( 13 | 'https://www.unpkg.com/css-houdini-squircle@0.2.1/squircle.min.js' 14 | ) 15 | } 16 | 17 | // Copy buttons. 18 | const copies = Array.from(document.querySelectorAll('button.copy-button')) 19 | const copyTemplate = document.createElement('template') 20 | const copiedTemplate = document.createElement('template') 21 | copyTemplate.innerHTML = ` 29 | Copy 30 | 35 | 40 | ` 41 | copiedTemplate.innerHTML = ` 49 | Copied! 50 | 55 | ` 56 | const copyIcon = copyTemplate.content.querySelector('svg') 57 | const copiedIcon = copiedTemplate.content.querySelector('svg') 58 | assert(copyIcon) 59 | assert(copiedIcon) 60 | 61 | for (const copy of copies) { 62 | assert(copy instanceof HTMLButtonElement) 63 | copy.type = 'button' 64 | copy.replaceChildren(copyIcon.cloneNode(true)) 65 | copy.addEventListener('click', oncopyonclick) 66 | } 67 | 68 | const popoverTargets = /** @type {Array} */ ( 69 | Array.from(document.querySelectorAll('.rehype-twoslash-popover-target')) 70 | ) 71 | 72 | for (const popoverTarget of popoverTargets) { 73 | /** @type {NodeJS.Timeout | number} */ 74 | let timeout = 0 75 | 76 | popoverTarget.addEventListener('click', function () { 77 | popoverShow(popoverTarget) 78 | }) 79 | 80 | popoverTarget.addEventListener('mouseenter', function () { 81 | clearTimeout(timeout) 82 | timeout = setTimeout(function () { 83 | popoverShow(popoverTarget) 84 | }, 300) 85 | }) 86 | 87 | popoverTarget.addEventListener('mouseleave', function () { 88 | clearTimeout(timeout) 89 | }) 90 | 91 | if (popoverTarget.classList.contains('rehype-twoslash-autoshow')) { 92 | popoverShow(popoverTarget) 93 | } 94 | } 95 | 96 | /** 97 | * @this {HTMLButtonElement} 98 | * Button element. 99 | * @returns {undefined} 100 | * Nothing. 101 | */ 102 | function oncopyonclick() { 103 | assert(copyIcon) 104 | assert(copiedIcon) 105 | assert(this instanceof HTMLButtonElement) 106 | 107 | const value = this.dataset.value 108 | assert(value !== undefined) 109 | 110 | this.classList.add('success') 111 | this.replaceChildren(copiedIcon.cloneNode(true)) 112 | 113 | copyToClipboard(value) 114 | 115 | setTimeout(() => { 116 | this.classList.remove('success') 117 | this.replaceChildren(copyIcon.cloneNode(true)) 118 | }, 2000) 119 | } 120 | 121 | /** 122 | * @param {HTMLElement} popoverTarget 123 | * Popover target. 124 | * @returns {undefined} 125 | * Nothing. 126 | */ 127 | function popoverShow(popoverTarget) { 128 | const id = popoverTarget.dataset.popoverTarget 129 | if (!id) return 130 | const popover = document.getElementById(id) 131 | if (!popover) return 132 | 133 | popover.showPopover() 134 | 135 | computePosition(popoverTarget, popover, { 136 | placement: 'bottom', 137 | middleware: [shift({padding: 5})] 138 | }).then( 139 | /** 140 | * @param {{x: number, y: number}} value 141 | */ 142 | function (value) { 143 | popover.style.left = value.x + 'px' 144 | popover.style.top = value.y + 'px' 145 | } 146 | ) 147 | } 148 | 149 | // Docsearch. 150 | // Note: types are wrong. 151 | const docsearch = /** @type {import('@docsearch/js')['default']} */ ( 152 | /** @type {unknown} */ (docsearch_) 153 | ) 154 | 155 | docsearch({ 156 | appId: 'B0O9AAZ9L2', 157 | apiKey: '71f38eae605e3e6d500368617e32c19f', 158 | container: '#docsearch', 159 | indexName: 'mdxjs' 160 | }) 161 | -------------------------------------------------------------------------------- /docs/_component/blog.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {ReactNode} from 'react' 3 | * @import {Item} from './sort.js' 4 | */ 5 | 6 | /** 7 | * @typedef EntryProperties 8 | * Properties for `BlogEntry`. 9 | * @property {Readonly} item 10 | * Item. 11 | * 12 | * @typedef GroupProperties 13 | * Properties for `BlogGroup`. 14 | * @property {string | undefined} [className] 15 | * Class name. 16 | * @property {ReadonlyArray} items 17 | * Items. 18 | * @property {string | undefined} [sort] 19 | * Fields to sort on. 20 | */ 21 | 22 | import {apStyleTitleCase} from 'ap-style-title-case' 23 | import {toJsxRuntime} from 'hast-util-to-jsx-runtime' 24 | import React from 'react' 25 | import {Fragment, jsx, jsxs} from 'react/jsx-runtime' 26 | import {sortItems} from './sort.js' 27 | 28 | const dateTimeFormat = new Intl.DateTimeFormat('en', {dateStyle: 'long'}) 29 | 30 | /** 31 | * @param {Readonly} properties 32 | * Properties. 33 | * @returns {ReactNode} 34 | * Element. 35 | */ 36 | export function BlogEntry(properties) { 37 | const {item} = properties 38 | const {data, name} = item 39 | const {matter = {}, meta = {}} = data 40 | const title = matter.title || meta.title 41 | const defaultTitle = apStyleTitleCase( 42 | name.replace(/\/$/, '').split('/').pop() 43 | ) 44 | const description = matter.description || meta.description 45 | const time = ( 46 | meta.readingTime 47 | ? Array.isArray(meta.readingTime) 48 | ? meta.readingTime 49 | : [meta.readingTime, meta.readingTime] 50 | : [] 51 | ).map(function (d) { 52 | return Math.ceil(d) 53 | }) 54 | /** @type {string | undefined} */ 55 | let timeLabel 56 | 57 | if (time.length > 1 && time[0] !== time[1]) { 58 | timeLabel = time[0] + '-' + time[1] + ' minutes' 59 | } else if (time[0]) { 60 | timeLabel = time[0] + ' minute' + (time[0] > 1 ? 's' : '') 61 | } 62 | 63 | return ( 64 |
65 |

66 | {title || defaultTitle} 67 |

68 |
69 | {meta.descriptionHast ? ( 70 | toJsxRuntime(meta.descriptionHast, {Fragment, jsx, jsxs}) 71 | ) : description ? ( 72 |

{description}

73 | ) : undefined} 74 | 75 | Continue reading » 76 | 77 |
78 |
82 |
83 | {meta.author ? ( 84 | <> 85 | By {meta.author} 86 |
87 | 88 | ) : undefined} 89 | Reading time: {timeLabel} 90 |
91 | {meta.published && typeof meta.published === 'object' ? ( 92 |
93 | 94 | Published on{' '} 95 | 98 | 99 |
100 | ) : undefined} 101 |
102 |
103 | ) 104 | } 105 | 106 | /** 107 | * @param {Readonly} properties 108 | * Properties. 109 | * @returns {ReactNode} 110 | * Element. 111 | */ 112 | export function BlogGroup(properties) { 113 | const { 114 | className, 115 | items, 116 | sort = 'navSortSelf,meta.title', 117 | ...rest 118 | } = properties 119 | const sorted = sortItems(items, sort) 120 | 121 | return ( 122 | <> 123 | {sorted.map(function (d) { 124 | return 125 | })} 126 | 127 | ) 128 | } 129 | -------------------------------------------------------------------------------- /docs/_component/foot-site.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {config} from '../_config.js' 3 | 4 | export function FootSite() { 5 | return ( 6 |
7 |
8 |
12 |
13 | 14 | MDX is made with ❤️ in Amsterdam, Boise, and around the 🌏 15 | 16 |
17 | This site does not track you. 18 |
19 | MIT © 2017-{new Date().getFullYear()} 20 |
21 |
22 | 23 | Project on GitHub 24 | 25 |
26 | 27 | Site on GitHub 28 | 29 |
30 | 31 | Updates as RSS feed 32 | 33 |
34 | 35 | Sponsor on OpenCollective 36 | 37 |
38 |
39 |
40 |
41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /docs/_component/home.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {ReactNode} from 'react' 3 | * @import {Data} from 'vfile' 4 | * @import {Item} from './sort.js' 5 | */ 6 | 7 | /** 8 | * @typedef {Exclude} Meta 9 | * 10 | * @typedef Properties 11 | * Properties. 12 | * @property {string} name 13 | * Name. 14 | * @property {ReactNode} children 15 | * Children. 16 | * @property {Item} navigationTree 17 | * Navigation tree. 18 | * @property {Meta} meta 19 | * Meta. 20 | */ 21 | 22 | import React from 'react' 23 | import {FootSite} from './foot-site.jsx' 24 | import {NavigationSite, NavigationSiteSkip} from './nav-site.jsx' 25 | 26 | /** 27 | * @param {Readonly} properties 28 | * Properties. 29 | * @returns {ReactNode} 30 | * Element. 31 | */ 32 | export function Home(properties) { 33 | const {children, meta, name, navigationTree} = properties 34 | 35 | return ( 36 |
37 | 38 |
39 | {meta.schemaOrg ? ( 40 | 43 | ) : undefined} 44 |
45 |
{children}
46 |
47 | 48 |
49 | 50 |
51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /docs/_component/icon/github.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export function GitHub() { 4 | return ( 5 | 13 | GitHub 14 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /docs/_component/icon/mdx.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export function Mdx() { 4 | return ( 5 | 13 | MDX 14 | 15 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /docs/_component/icon/open-collective.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export function OpenCollective() { 4 | return ( 5 | 13 | OpenCollective 14 | 18 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /docs/_component/nav-site.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {ReactNode} from 'react' 3 | * @import {Item} from './sort.js' 4 | */ 5 | 6 | /** 7 | * @typedef Properties 8 | * Properties. 9 | * @property {string} name 10 | * Name. 11 | * @property {Readonly} navigationTree 12 | * Navigation tree. 13 | */ 14 | 15 | import React from 'react' 16 | import {config} from '../_config.js' 17 | import {GitHub} from './icon/github.jsx' 18 | import {Mdx} from './icon/mdx.jsx' 19 | import {OpenCollective} from './icon/open-collective.jsx' 20 | import {NavigationGroup} from './nav.jsx' 21 | 22 | export function NavigationSiteSkip() { 23 | return ( 24 | 29 | Skip to navigation 30 | 31 | ) 32 | } 33 | 34 | /** 35 | * @param {Readonly} properties 36 | * Properties. 37 | * @returns {ReactNode} 38 | * Element. 39 | */ 40 | export function NavigationSite(properties) { 41 | const {name, navigationTree} = properties 42 | 43 | return ( 44 | 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /docs/_component/nav.jsx: -------------------------------------------------------------------------------- 1 | // Augment vfile data: 2 | /// 3 | 4 | /** 5 | * @import {ElementContent} from 'hast' 6 | * @import {ReactNode} from 'react' 7 | * @import {Item} from './sort.js' 8 | */ 9 | 10 | /** 11 | * @typedef ItemProperties 12 | * Properties for `NavigationItem`. 13 | * @property {boolean | undefined} [includeDescription=false] 14 | * Whether to include the description (default: `false`). 15 | * @property {boolean | undefined} [includePublished=false] 16 | * Whether to include the published date (default: `false`). 17 | * @property {Readonly} item 18 | * Item. 19 | * @property {string | undefined} [name] 20 | * Name. 21 | * 22 | * @typedef GroupOnlyProperties 23 | * Properties for `NavigationGroup`; 24 | * Other fields are passed to `NavigationItem`. 25 | * @property {string | undefined} [className] 26 | * Class name. 27 | * @property {ReadonlyArray} items 28 | * Items. 29 | * @property {string | undefined} [sort] 30 | * Fields to sort on. 31 | * @property {string | undefined} [name] 32 | * Name. 33 | * 34 | * @typedef {Omit & GroupOnlyProperties} GroupProperties 35 | * Properties for `NavigationGroup`. 36 | */ 37 | 38 | import {apStyleTitleCase} from 'ap-style-title-case' 39 | import {toJsxRuntime} from 'hast-util-to-jsx-runtime' 40 | import React from 'react' 41 | import {Fragment, jsx, jsxs} from 'react/jsx-runtime' 42 | import {sortItems} from './sort.js' 43 | 44 | const dateTimeFormat = new Intl.DateTimeFormat('en', {dateStyle: 'long'}) 45 | 46 | /** 47 | * @param {Readonly} properties 48 | * Properties. 49 | * @returns {ReactNode} 50 | * Element. 51 | */ 52 | export function NavigationGroup(properties) { 53 | const { 54 | className, 55 | items, 56 | sort = 'navSortSelf,meta.title', 57 | ...rest 58 | } = properties 59 | 60 | return ( 61 |
    62 | {sortItems(items, sort).map(function (d) { 63 | return 64 | })} 65 |
66 | ) 67 | } 68 | 69 | /** 70 | * @param {Readonly} properties 71 | * Properties. 72 | * @returns {ReactNode} 73 | * Element. 74 | */ 75 | export function NavigationItem(properties) { 76 | const { 77 | includeDescription, 78 | includePublished, 79 | item, 80 | name: activeName 81 | } = properties 82 | const {children, data = {}, name} = item 83 | const {matter = {}, meta = {}, navExcludeGroup, navigationSortItems} = data 84 | const title = matter.title || meta.title 85 | const defaultTitle = apStyleTitleCase( 86 | name.replace(/\/$/, '').split('/').pop() 87 | ) 88 | /** @type {ReactNode} */ 89 | let description 90 | /** @type {string | undefined} */ 91 | let published 92 | 93 | if (includeDescription) { 94 | if (meta.descriptionHast) { 95 | // Cast because we don’t expect doctypes. 96 | const children = /** @type {Array} */ ( 97 | meta.descriptionHast.children 98 | ) 99 | 100 | description = toJsxRuntime( 101 | { 102 | type: 'element', 103 | tagName: 'div', 104 | properties: {className: ['nav-description']}, 105 | children 106 | }, 107 | {Fragment, jsx, jsxs} 108 | ) 109 | } else { 110 | description = matter.description || meta.description || undefined 111 | 112 | description &&= ( 113 |
114 |

{description}

115 |
116 | ) 117 | } 118 | } 119 | 120 | const pub = matter.published || meta.published 121 | 122 | if (includePublished && pub) { 123 | published = dateTimeFormat.format( 124 | typeof pub === 'string' ? new Date(pub) : pub || undefined 125 | ) 126 | } 127 | 128 | return ( 129 |
  • 130 | {title ? ( 131 | 132 | {title} 133 | 134 | ) : ( 135 | defaultTitle 136 | )} 137 | {published ? ' — ' + published : undefined} 138 | {description || undefined} 139 | {!navExcludeGroup && children.length > 0 ? ( 140 | 149 | ) : undefined} 150 |
  • 151 | ) 152 | } 153 | -------------------------------------------------------------------------------- /docs/_component/note.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {ReactNode} from 'react' 3 | */ 4 | 5 | /** 6 | * @typedef {'important' | 'info' | 'legacy'} NoteType 7 | * Type. 8 | * 9 | * @typedef Properties 10 | * Properties for `Note`. 11 | * @property {NoteType} type 12 | * Kind. 13 | * @property {Readonly} children 14 | * Children. 15 | */ 16 | 17 | import React from 'react' 18 | 19 | /** @type {Set} */ 20 | const known = new Set(['info', 'legacy', 'important']) 21 | 22 | /** 23 | * @param {Readonly} properties 24 | * Properties. 25 | * @returns {ReactNode} 26 | * Element. 27 | */ 28 | export function Note(properties) { 29 | const {children, type} = properties 30 | const className = ['note'] 31 | 32 | if (known.has(type)) className.push(type) 33 | else { 34 | throw new Error('Unknown note type `' + type + '`') 35 | } 36 | 37 | return
    {children}
    38 | } 39 | -------------------------------------------------------------------------------- /docs/_component/snowfall.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {ReactNode} from 'react' 3 | */ 4 | 5 | /** 6 | * @typedef Properties 7 | * Properties. 8 | * @property {string} color 9 | * Color. 10 | * @property {number} year 11 | * Year. 12 | */ 13 | 14 | import React from 'react' 15 | 16 | const data = [6, 5, 2, 4.5, 1.5, 2.5, 2, 2.5, 1.5, 2.5, 3.5, 7] 17 | 18 | /** 19 | * @param {Readonly} properties 20 | * Properties. 21 | * @returns {ReactNode} 22 | * Element. 23 | */ 24 | export function Chart(properties) { 25 | return ( 26 |
    27 | {data.map(function (d) { 28 | return ( 29 |
    37 | ) 38 | })} 39 |
    40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /docs/_component/sort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Data} from 'vfile' 3 | */ 4 | 5 | /** 6 | * @typedef Item 7 | * Item. 8 | * @property {string} name 9 | * Name. 10 | * @property {Readonly} data 11 | * Data. 12 | * @property {Array} children 13 | * Children. 14 | */ 15 | 16 | import dlv from 'dlv' 17 | 18 | const collator = new Intl.Collator('en').compare 19 | 20 | /** 21 | * @param {ReadonlyArray} items 22 | * Items. 23 | * @param {string | undefined} [sortString] 24 | * Fields to sort on (default: `'navSortSelf,meta.title'`). 25 | * @returns {ReadonlyArray} 26 | * Items. 27 | */ 28 | export function sortItems(items, sortString = 'navSortSelf,meta.title') { 29 | /** @type {ReadonlyArray<[string, 'asc' | 'desc']>} */ 30 | const fields = sortString.split(',').map(function (d) { 31 | const [field, order = 'asc'] = d.split(':') 32 | 33 | if (order !== 'asc' && order !== 'desc') { 34 | throw new Error('Cannot order as `' + order + '`') 35 | } 36 | 37 | return [field, order] 38 | }) 39 | 40 | return [...items].sort(function (left, right) { 41 | let index = -1 42 | 43 | while (++index < fields.length) { 44 | const [field, order] = fields[index] 45 | /** @type {unknown} */ 46 | let a = dlv(left.data, field) 47 | /** @type {unknown} */ 48 | let b = dlv(right.data, field) 49 | 50 | // Dates. 51 | if (a && typeof a === 'object' && 'valueOf' in a) a = a.valueOf() 52 | if (b && typeof b === 'object' && 'valueOf' in b) b = b.valueOf() 53 | 54 | const score = 55 | typeof a === 'string' && typeof b === 'string' 56 | ? collator(a, b) 57 | : typeof a === 'number' && typeof b === 'number' 58 | ? a - b 59 | : (a === null || a === undefined) && (b === null || b === undefined) 60 | ? 0 61 | : a === null || a === undefined 62 | ? 1 63 | : b === null || b === undefined 64 | ? -1 65 | : 0 66 | 67 | if (score) return order === 'asc' ? score : -score 68 | } 69 | 70 | return 0 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /docs/_config.js: -------------------------------------------------------------------------------- 1 | const site = new URL('https://mdxjs.com') 2 | const git = new URL('../', import.meta.url) 3 | const gh = new URL('https://github.com/mdx-js/mdx/') 4 | 5 | export const config = { 6 | author: 'MDX contributors', 7 | color: '#010409', 8 | gh, 9 | ghBlob: new URL('blob/main/', gh), 10 | ghTree: new URL('tree/main/', gh), 11 | git, 12 | input: new URL('docs/', git), 13 | oc: new URL('https://opencollective.com/unified'), 14 | output: new URL('public/', git), 15 | site, 16 | tags: ['mdx', 'markdown', 'jsx', 'oss', 'react'], 17 | title: 'MDX' 18 | } 19 | 20 | /** @type {Record} */ 21 | export const redirect = { 22 | '/about/index.html': '/community/about/', 23 | '/advanced/index.html': '/guides/', 24 | '/advanced/api/index.html': '/packages/mdx/#api', 25 | '/advanced/ast/index.html': '/packages/remark-mdx/#syntax-tree', 26 | '/advanced/components/index.html': '/docs/using-mdx/', 27 | '/advanced/contributing/index.html': '/community/contribute/', 28 | '/advanced/custom-loader/index.html': '/guides/frontmatter/', 29 | '/advanced/retext-plugins/index.html': '/docs/extending-mdx/#using-plugins', 30 | '/advanced/plugins/index.html': '/docs/extending-mdx/', 31 | '/advanced/runtime/index.html': '/packages/mdx/#evaluatefile-options', 32 | '/advanced/specification/index.html': '/packages/remark-mdx/#syntax-tree', 33 | '/advanced/sync-api/index.html': '/packages/mdx/#api', 34 | '/advanced/transform-content/index.html': '/packages/remark-mdx/', 35 | '/advanced/typescript/index.html': '/docs/getting-started/#types', 36 | '/advanced/writing-a-plugin/index.html': '/guides/frontmatter/', 37 | '/contributing/index.html': '/community/contribute/', 38 | '/editor-plugins/index.html': '/docs/getting-started/#editor', 39 | '/editors/index.html': '/docs/getting-started/#editor', 40 | '/getting-started/create-react-app/index.html': '/docs/getting-started/#vite', 41 | '/getting-started/gatsby/index.html': '/docs/getting-started/#gatsby', 42 | '/getting-started/next/index.html': '/docs/getting-started/#nextjs', 43 | '/getting-started/parcel/index.html': '/docs/getting-started/#parcel', 44 | '/getting-started/react-static/index.html': '/docs/getting-started/#vite', 45 | '/getting-started/table-of-components/index.html': '/table-of-components/', 46 | '/getting-started/typescript/index.html': '/docs/getting-started/#types', 47 | '/getting-started/webpack/index.html': '/docs/getting-started/#webpack', 48 | '/getting-started/index.html': '/docs/getting-started/', 49 | '/guides/custom-loader/index.html': '/guides/frontmatter/', 50 | '/guides/live-code/index.html': 51 | '/guides/syntax-highlighting/#syntax-highlighting-with-the-meta-field', 52 | '/guides/markdown-in-components/index.html': '/docs/what-is-mdx/', 53 | '/guides/math-blocks/index.html': '/guides/math/', 54 | '/guides/mdx-embed/index.html': '/guides/embed/#embeds-at-run-time', 55 | '/guides/table-of-contents/index.html': '/docs/extending-mdx/', 56 | '/guides/terminal/index.html': '/docs/getting-started/#ink', 57 | '/guides/vue/index.html': '/docs/getting-started/#vue', 58 | '/guides/wrapper-customization/index.html': '/docs/using-mdx/#layout', 59 | '/guides/writing-a-plugin/index.html': 60 | '/docs/extending-mdx/#creating-plugins', 61 | '/mdx/index.html': '/docs/what-is-mdx/', 62 | '/plugins/index.html': '/docs/extending-mdx/#using-plugins', 63 | '/projects/index.html': '/community/projects/', 64 | '/support/index.html': '/community/support/', 65 | '/syntax/index.html': '/docs/getting-started/#syntax', 66 | '/vue/index.html': '/docs/getting-started/#vue' 67 | } 68 | -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdx-js/mdx/b3351fadcb6f78833a72757b7135dcfb8ab646fe/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | MDX 3 | 11 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/_static/og-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdx-js/mdx/b3351fadcb6f78833a72757b7135dcfb8ab646fe/docs/_static/og-v2.png -------------------------------------------------------------------------------- /docs/_static/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdx-js/mdx/b3351fadcb6f78833a72757b7135dcfb8ab646fe/docs/_static/og.png -------------------------------------------------------------------------------- /docs/blog/conf.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'johno', name: 'John Otander'} 6 | ], 7 | modified: new Date('2025-01-27'), 8 | published: new Date('2020-07-31') 9 | } 10 | 11 | 12 | **Note**: This is an old blog post. 13 | The below is kept as is for historical purposes. 14 | 15 | 16 | # MDXConf 17 | 18 | MDXConf is a free and online conference for the MDX community. 19 | Whether you’re just learning about MDX or an expert, there’ll be something for 20 | you! {/* more */} 21 | 22 | August 24th, 2020 at 8am PDT/3pm UST
    Online • Free 23 | 24 | ## Watch 25 | 26 | The conference is now over, but you can still watch the recordings! 27 | 28 | [Watch the talks →](https://egghead.io/playlists/mdx-conf-3fc2) 29 | 30 | ## About 31 | 32 | Join us for the first MDX conference! 33 | We’ll stream it directly to you, for free. 34 | 35 | MDX has grown rapidly since the [first commit][] two and a half years ago. 36 | We’d like to celebrate our accomplishments so far, and talk about what lies 37 | ahead. 38 | We’ve got lots of plans. 39 | 40 | Learn how MDX increases developer productivity, improves educational 41 | content authoring, and even peek behind the curtains to see how MDX works. 42 | 43 | ## Speakers 44 | 45 | ### Chris Biscardi 46 | 47 | ![](https://github.com/ChristopherBiscardi.png?size=200)] 48 | 49 | Keynote: The past, present, and future of MDX 50 | 51 | ### Monica Powell 52 | 53 | ![](https://github.com/M0nica.png?size=200) 54 | 55 | Migrating to MDX 56 | 57 | ### Laurie Barth 58 | 59 | ![](https://github.com/laurieontech.png?size=200) 60 | 61 | MDX v2 syntax 62 | 63 | ### Cole Bemis 64 | 65 | ![](https://github.com/colebemis.png?size=200) 66 | 67 | Demystifying MDX 68 | 69 | ### Prince Wilson 70 | 71 | ![](https://github.com/maxcell.png?size=200) 72 | 73 | Personal site playgrounds 74 | 75 | ### Kathleen McMahon 76 | 77 | ![](https://github.com/resource11.png?size=200) 78 | 79 | Digital gardening with MDX magic 80 | 81 | ### Rodrigo Pombo 82 | 83 | ![](https://github.com/pomber.png?size=200) 84 | 85 | The X in MDX 86 | 87 | ### Jonathan Bakebwa 88 | 89 | ![](https://github.com/codebender828.png?size=200) 90 | 91 | MDX and Vue/Nuxt 92 | 93 | ## Sign up 94 | 95 | 96 | **Note**: Sign up is closed. 97 | 98 | 99 | ## FAQ 100 | 101 | ### What if I can’t make it on August 24th? 102 | 103 | We’ll miss you, but you won’t miss out! 104 | All talks will be recorded and released the day of the conference. 105 | You can catch up with the talks, or rewatch them, whenever convenient. 106 | 107 | ### Will the talks be transcribed? 108 | 109 | Yes. 110 | 111 | ### Is there a code of conduct? 112 | 113 | Absolutely. 114 | We’re dedicated to providing a harassment-free experience for everyone. 115 | We will not tolerate harassment of participants in any form. 116 | We’ve adopted the [Party Corgi Network’s Code of Conduct][coc]. 117 | We will have moderators to ensure that the code of conduct is followed. 118 | 119 | ### Do you have a different question? 120 | 121 | Reach out to us. 122 | 123 | [coc]: https://github.com/partycorgi/partycorgi/blob/corgi/CODE_OF_CONDUCT.md 124 | 125 | [first commit]: https://github.com/mdx-js/mdx/commit/dee47dc20b08d534132e3b966cdccf3b88c7bca5 126 | -------------------------------------------------------------------------------- /docs/blog/index.mdx: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * @import {Item} from '../_component/sort.js' 4 | */ 5 | 6 | /** 7 | * @typedef Props 8 | * @property {Item} navigationTree 9 | */ 10 | } 11 | 12 | import assert from 'node:assert/strict' 13 | import {BlogGroup} from '../_component/blog.jsx' 14 | 15 | export const info = { 16 | author: [{name: 'MDX Contributors'}], 17 | modified: new Date('2024-07-04'), 18 | published: new Date('2021-11-01') 19 | } 20 | export const navExcludeGroup = true 21 | export const navigationSortItems = 'navSortSelf,meta.published:desc' 22 | export const navSortSelf = 7 23 | 24 | # Blog 25 | 26 | The latest news about MDX. 27 | 28 | { 29 | (function () { 30 | const navigationTree = props.navigationTree 31 | const category = navigationTree.children.find(function (item) { 32 | return item.name === '/blog/' 33 | }) 34 | assert(category) 35 | 36 | return ( 37 | 40 | ) 41 | })() 42 | } 43 | -------------------------------------------------------------------------------- /docs/blog/shortcodes.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'johno', name: 'John Otander'} 6 | ], 7 | modified: new Date('2021-11-01'), 8 | published: new Date('2019-05-14') 9 | } 10 | 11 | 12 | **Note**: This is an old blog post. 13 | The features described in it are currently documented at 14 | [§ MDX provider](/docs/using-mdx/#mdx-provider). 15 | The below is kept as is for historical purposes. 16 | 17 | 18 | # Shortcodes 19 | 20 | An exciting new feature in MDX v1 is global shortcodes. 21 | This allows you to expose components to all of your documents in your app or 22 | website. 23 | This is a useful feature for common components like YouTube embeds, Twitter 24 | cards, or anything else frequently used in your documents. 25 | 26 | {/* more */} 27 | 28 | If you have an application wrapper for your MDX documents 29 | you can add in components with the `MDXProvider`: 30 | 31 | ```tsx path="src/App.js" 32 | import React from 'react' 33 | import {MDXProvider} from '@mdx-js/react' 34 | import {TomatoBox, Twitter, YouTube} from './ui' 35 | 36 | const shortcodes = {TomatoBox, Twitter, YouTube} 37 | 38 | export default ({children}) => ( 39 | {children} 40 | ) 41 | ``` 42 | 43 | Then, any MDX document that’s wrapped in `App` has access to `YouTube`, 44 | `Twitter`, and `TomatoBox`. 45 | Shortcodes are nothing more than components, so you can reference them anywhere 46 | in an MDX document with JSX. 47 | 48 | ```mdx path="example.mdx" 49 | # Hello world! 50 | 51 | Here’s a YouTube shortcode: 52 | 53 | 54 | 55 | Here’s a YouTube shortcode wrapped in TomatoBox: 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | That’s it. 63 | 🎉 🚀 64 | 65 | Huge thanks to [Chris Biscardi](https://christopherbiscardi.com) 66 | for building out most of this functionality. 67 | -------------------------------------------------------------------------------- /docs/blog/v3.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | export const info = { 3 | author: [ 4 | {github: 'wooorm', name: 'Titus Wormer'} 5 | ], 6 | modified: new Date('2025-01-27'), 7 | published: new Date('2023-10-24') 8 | } 9 | 10 | 11 | **Note**: Info on how to migrate is available in our 12 | [Version 3 migration guide][migrating]. 13 | 14 | 15 | # MDX 3 16 | 17 | Version 3 already! 18 | This major version contains a couple small changes. 19 | For most folks, updating Node.js and plugins is all that’s needed! 20 | 21 | {/* more */} 22 | 23 | ## Contents 24 | 25 | * [Breaking changes](#breaking-changes) 26 | * [Improvements to the MDX format](#improvements-to-the-mdx-format) 27 | * [Adjacent block JSX and expressions in MDX](#adjacent-block-jsx-and-expressions-in-mdx) 28 | * [Await in MDX](#await-in-mdx) 29 | * [ES2024 in MDX](#es2024-in-mdx) 30 | * [Miscellaneous](#miscellaneous) 31 | * [Thanks](#thanks) 32 | 33 | ## Breaking changes 34 | 35 | The main breaking change is that Node.js 16 is now the minimum supported 36 | version. 37 | 38 | Across the ecosystem there were several small internal breaking changes. 39 | Everything’s released already. 40 | You can update all plugins now. 41 | If you ran into problems before, it should work now. 42 | 43 | We also removed some infrequently used deprecated APIs. 44 | You’re likely fine but gloss over the [v3 migration guide][migrating] if you 45 | get errors. 46 | 47 | Important to note when you use your lesser-known but powerful `evaluate`, `run`, 48 | or `outputFormat: 'function-body'` APIs, please pass the `baseUrl` option. 49 | That makes sure `import.meta.url`, `import`, and `export` work. 50 | You’ll get a runtime error when those features are used otherwise. 51 | 52 | ## Improvements to the MDX format 53 | 54 | There’s also a few small improvements to the MDX format, some of which 55 | technically breaking. 56 | 57 | ### Adjacent block JSX and expressions in MDX 58 | 59 | We now accept block expressions right next to block JSX tags: 60 | 61 | ```mdx chrome=no 62 | 69 | ``` 70 | 71 | Previously, there was a syntax error, and you had to add a newline between the 72 | angle brackets and the braces. 73 | 74 | ### Await in MDX 75 | 76 | We now accept `await` syntax: 77 | 78 | ```mdx 79 | {await Promise.resolve(42)} 80 | ``` 81 | 82 | Most frameworks don’t support promises. 83 | Whether this works depends on that. 84 | 85 | Previously, there was a runtime error that `await` was used in a context where 86 | it wasn’t allowed. 87 | 88 | ### ES2024 in MDX 89 | 90 | You can now use modern JavaScript syntax in MDX. 91 | Acorn, used internally, is now instructed to use ES2024. 92 | 93 | ## Miscellaneous 94 | 95 | I refactored all the docs. 96 | Updating every use example where needed. 97 | I also wrote a guide on how to inject components from anywhere: 98 | [§ Injecting components][injecting-components]. 99 | 100 | The site is a lot faster. 101 | There’s a nice improved playground too: [try it out! »][playground]. 102 | We also have proper syntax highlighting here, thanks to 103 | [`wooorm/markdown-tm-language`][markdown-tm-language] 104 | and [`wooorm/starry-night`][starry-night]. 105 | 106 | The generated JS code is a little cleaner (the JSX pragma comment is removed 107 | and objects are sorted where needed), it also uses spreads instead of 108 | `Object.assign`, there’s a `'use strict'` added when needed, and the 109 | `MDXContent` is exported immediately. 110 | 111 | ## Thanks 112 | 113 | We’d like to say thanks to all our contributors and our happy users. 114 | Special thanks to 115 | 北雁云依 ([**@BeiyanYunyi**](https://github.com/BeiyanYunyi)), 116 | Christian Murphy ([**@ChristianMurphy**](https://github.com/ChristianMurphy)), 117 | JokcyLou ([**@Jokcy**](https://github.com/Jokcy)), 118 | Maël Nison ([**@arcanis**](https://github.com/arcanis)), 119 | Andreas Deininger ([**@deining**](https://github.com/deining)), 120 | Remco Haszing ([**@remcohaszing**](https://github.com/remcohaszing)), 121 | Sébastien Lorber ([**@slorber**](https://github.com/slorber)), 122 | Víctor Fernández ([**@victor23k**](https://github.com/victor23k)), 123 | Titus Wormer ([**@wooorm**](https://github.com/wooorm)), 124 | and anyone we may have forgotten. 125 | 126 | [injecting-components]: /guides/injecting-components/ 127 | 128 | [markdown-tm-language]: https://github.com/wooorm/markdown-tm-language 129 | 130 | [migrating]: /migrating/v3/ 131 | 132 | [playground]: /playground/ 133 | 134 | [starry-night]: https://github.com/wooorm/starry-night 135 | -------------------------------------------------------------------------------- /docs/community/contribute.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'wooorm', name: 'Titus Wormer'} 6 | ], 7 | modified: new Date('2025-01-27'), 8 | published: new Date('2018-11-04') 9 | } 10 | export const navSortSelf = 2 11 | 12 | # Contribute 13 | 14 | This article explains how to contribute to MDX. 15 | Please read through the following guidelines. 16 | 17 | {/* more */} 18 | 19 | 20 | **Important**: before participating in our community, please read our 21 | [code of conduct][coc]. 22 | By interacting with this repository, organization, or community you agree to 23 | abide by its terms. 24 | 25 | 26 | ## Contributions 27 | 28 | There’s several ways to contribute, not just by writing code. 29 | If you have questions, see [§ Support][support]. 30 | If you can provide financial support, see [§ Sponsor][sponsor]. 31 | 32 | ### Improve docs 33 | 34 | As a user you’re perfect for helping us improve our docs. 35 | Typo corrections, error fixes, better explanations, new examples, etcetera. 36 | All MDX docs live in `docs/`. 37 | 38 | You can run the docs locally, see [¶ Site][site] below. 39 | 40 | ### Improve issues 41 | 42 | Some issues lack information, aren’t reproducible, or are just incorrect. 43 | You can help by trying to make them easier to resolve. 44 | Existing issues might benefit from your unique experience or opinions. 45 | 46 | ### Write code 47 | 48 | Code contributions are very welcome. 49 | It’s probably a good idea to first post a question or open an issue to report a 50 | bug or suggest a new feature before creating a pull request. 51 | 52 | ## Submitting an issue 53 | 54 | * The issue tracker is for issues. 55 | Use discussions for support 56 | * Search the issue tracker (including closed issues) before opening a new 57 | issue 58 | * Ensure you’re using the latest version of our packages 59 | * Use a clear and descriptive title 60 | * Include as much information as possible: steps to reproduce the issue, 61 | error message, version, operating system, etcetera 62 | * The more time you put into an issue, the better we will be able to help you 63 | * The best issue report is a failing test proving it 64 | 65 | ## Submitting a pull request 66 | 67 | * See [¶ Project][project] below for info on how the project is structured, 68 | how to test, and how to build the site 69 | * Non-trivial changes are often best discussed in an issue first, to prevent 70 | you from doing unnecessary work 71 | * For ambitious tasks, you should try to get your work in front of the 72 | community for feedback as soon as possible 73 | * New features should be accompanied by tests and documentation 74 | * Don’t include unrelated changes 75 | * Test before submitting code by running `npm test` 76 | * Write a convincing description of why we should land your pull request: 77 | it’s your job to convince us 78 | 79 | ## Project 80 | 81 | ### Structure 82 | 83 | MDX is a monorepo. 84 | All packages are in `packages/`. 85 | Documentation is in `docs/`. 86 | 87 | ### Tests 88 | 89 | To run the tests, first do `npm install`, then do `npm test`. 90 | This ensures everything is okay, from code style to unit tests to types. 91 | 92 | ### Site 93 | 94 | To build the site, first do `npm install`, then do `npm run docs`. 95 | This produces the website in `public/`. 96 | 97 | ### Release 98 | 99 | To release a new version, do: 100 | 101 | 1. update `version`s of packages with a patch, minor, or major (make sure to 102 | update dependency ranges on monorepo packages when needed): 103 | ```sh 104 | npm version minor --workspaces --no-git-tag-version 105 | ``` 106 | 2. commit and tag using the version (without `v`) as the message: 107 | ```sh 108 | git commit --all --message 1.2.3 && git tag 1.2.3 && git push && git push --tags 109 | ``` 110 | 3. release to the npm registry: 111 | ```sh 112 | npm publish --workspaces 113 | ``` 114 | 4. add a changelog entry for the release on GitHub: 115 | ```sh 116 | open https://github.com/mdx-js/mdx/releases 117 | ``` 118 | 119 | ## Resources 120 | 121 | * [Good first issues in the MDX repository](https://github.com/mdx-js/mdx/labels/good%20first%20issue%20👋) 122 | * [How to contribute to open source](https://opensource.guide/how-to-contribute/) 123 | * [Making your first contribution](https://medium.com/@vadimdemedes/making-your-first-contribution-de6576ddb190) 124 | * [Using pull requests](https://help.github.com/articles/about-pull-requests/) 125 | * [GitHub help](https://help.github.com) 126 | 127 | [coc]: https://github.com/mdx-js/.github/blob/main/code-of-conduct.md 128 | 129 | [project]: #project 130 | 131 | [site]: #site 132 | 133 | [sponsor]: /community/sponsor/ 134 | 135 | [support]: /community/support/ 136 | -------------------------------------------------------------------------------- /docs/community/index.mdx: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * @import {Item} from '../_component/sort.js' 4 | */ 5 | 6 | /** 7 | * @typedef Props 8 | * @property {Item} navigationTree 9 | */ 10 | } 11 | 12 | import assert from 'node:assert/strict' 13 | import {NavigationGroup} from '../_component/nav.jsx' 14 | 15 | export const info = { 16 | author: [{name: 'MDX Contributors'}], 17 | modified: new Date('2024-07-04'), 18 | published: new Date('2021-11-01') 19 | } 20 | export const navSortSelf = 6 21 | 22 | # Community 23 | 24 | These pages explain how to contribute, get help, sponsor us, share your work, 25 | and some background information. 26 | 27 | { 28 | (function () { 29 | const navigationTree = props.navigationTree 30 | const category = navigationTree.children.find(function (item) { 31 | return item.name === '/community/' 32 | }) 33 | assert(category) 34 | 35 | return ( 36 | 39 | ) 40 | })() 41 | } 42 | -------------------------------------------------------------------------------- /docs/community/projects.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'johno', name: 'John Otander'} 6 | ], 7 | modified: new Date('2021-11-01'), 8 | published: new Date('2018-08-11') 9 | } 10 | export const navSortSelf = 5 11 | 12 | # Projects 13 | 14 | 15 | **Note**: have another project built with MDX? 16 | Please send a PR to add it here! 17 | 18 | 19 | This page lists community projects using MDX. 20 | 21 | {/* more */} 22 | 23 | ## Apps 24 | 25 | * [demoboard][]: The simplest editor alive 26 | 27 | ## Libraries 28 | 29 | * [ok-mdx][]: Browser-based MDX editor 30 | * [docz][]: Documentation framework 31 | * [mdx-deck][]: MDX-based presentation decks 32 | * [mdx-docs][]: Next-based documentation framework 33 | * [mdx-paper][]: MDX-based research articles 34 | * [spectacle-boilerplate-mdx][]: Boilerplate that facilitates using MDX with 35 | Spectacle 36 | * [Charge][]: An opinionated, zero-config static site generator 37 | * [MDNEXT][]: An ecosystem of tools to get your NextJS + MDX projects blasting 38 | off 39 | 40 | ## Sites 41 | 42 | * This website! 43 | * [Prisma][] 44 | * [Max Stoiber’s Blog][mxstbr] 45 | 46 | ## Other related links 47 | 48 | * [awesome-mdx][] 49 | * [MDX: content for kings and princesses][mdx-fairy-tale] 50 | 51 | [awesome-mdx]: https://github.com/transitive-bullshit/awesome-mdx 52 | 53 | [charge]: https://charge.js.org 54 | 55 | [demoboard]: https://frontarm.com/demoboard 56 | 57 | [docz]: https://www.docz.site/ 58 | 59 | [mdnext]: https://github.com/domitriusclark/mdnext 60 | 61 | [mdx-deck]: https://github.com/jxnblk/mdx-deck 62 | 63 | [mdx-docs]: https://github.com/jxnblk/mdx-docs 64 | 65 | [mdx-fairy-tale]: https://github.com/DeveloperMode/mdx-fairy-tale 66 | 67 | [mdx-paper]: https://github.com/hubgit/mdx-paper 68 | 69 | [mxstbr]: https://mxstbr.com 70 | 71 | [ok-mdx]: https://github.com/jxnblk/ok-mdx 72 | 73 | [prisma]: https://www.prisma.io/docs 74 | 75 | [spectacle-boilerplate-mdx]: https://github.com/FormidableLabs/spectacle-boilerplate-mdx 76 | -------------------------------------------------------------------------------- /docs/community/support.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'wooorm', name: 'Titus Wormer'} 6 | ], 7 | modified: new Date('2025-01-27'), 8 | published: new Date('2019-07-03') 9 | } 10 | export const navSortSelf = 1 11 | 12 | # Support 13 | 14 | This article explains where to get help with MDX. 15 | Please read through the following guidelines. 16 | 17 | {/* more */} 18 | 19 | 20 | **Important**: before participating in our community, please read our 21 | [code of conduct][coc]. 22 | By interacting with this repository, organization, or community you agree to 23 | abide by its terms. 24 | 25 | 26 | ## Asking quality questions 27 | 28 | Questions can go to [GitHub Discussions][chat]. 29 | 30 | Help us help you! 31 | Spend time framing questions and add links and resources. 32 | Spending the extra time up front can help save everyone time in the long run. 33 | Here are some tips: 34 | 35 | * Read through [§ Getting started][getting-started] 36 | * [Talk to a duck!][rubberduck] 37 | * Don’t fall for the [XY problem][xy] 38 | * Search to find out if a similar question has been asked 39 | * Try to define what you need help with: 40 | * Is there something in particular you want? 41 | * What problem are you encountering and what steps have you taken to try 42 | and fix it? 43 | * Is there a concept you don’t understand? 44 | * Provide sample code, such as a [CodeSandbox][cs] or video, if possible 45 | * Screenshots can help, but if there’s important text such as code or error 46 | messages in them, please also provide those as text 47 | * The more time you put into asking your question, the better we can help you 48 | 49 | [chat]: https://github.com/mdx-js/mdx/discussions 50 | 51 | [coc]: https://github.com/mdx-js/.github/blob/main/code-of-conduct.md 52 | 53 | [cs]: https://codesandbox.io 54 | 55 | [getting-started]: /docs/getting-started/ 56 | 57 | [rubberduck]: https://rubberduckdebugging.com 58 | 59 | [xy]: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/66378#66378 60 | -------------------------------------------------------------------------------- /docs/docs/index.mdx: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * @import {Item} from '../_component/sort.js' 4 | */ 5 | 6 | /** 7 | * @typedef Props 8 | * @property {Item} navigationTree 9 | */ 10 | } 11 | 12 | import assert from 'node:assert/strict' 13 | import {NavigationGroup} from '../_component/nav.jsx' 14 | 15 | export const info = { 16 | author: [{name: 'MDX Contributors'}], 17 | modified: new Date('2024-07-04'), 18 | published: new Date('2021-11-01') 19 | } 20 | export const navSortSelf = 1 21 | 22 | # Docs 23 | 24 | These docs explain the core concepts of MDX. 25 | How the format works, how to add it to your site, how to use MDX files, and how 26 | to extend them. 27 | Reading through these should give you a good understanding of MDX. 28 | 29 | { 30 | (function () { 31 | const navigationTree = props.navigationTree 32 | const category = navigationTree.children.find(function (item) { 33 | return item.name === '/docs/' 34 | }) 35 | assert(category) 36 | 37 | return ( 38 | 41 | ) 42 | })() 43 | } 44 | -------------------------------------------------------------------------------- /docs/guides/embed.mdx: -------------------------------------------------------------------------------- 1 | export const info = { 2 | author: [ 3 | {github: 'wooorm', name: 'Titus Wormer'} 4 | ], 5 | modified: new Date('2025-01-27'), 6 | published: new Date('2021-10-06') 7 | } 8 | export const navSortSelf = 5 9 | 10 | # Embed 11 | 12 | This guide explores how to embed things like tweets, gists or codepens in 13 | markdown. {/* more */} 14 | MDX supports standard markdown syntax ([CommonMark][]). 15 | It does not support embeds by default. 16 | 17 | There are two ways to accomplish embeds: at compile time or at runtime. 18 | Doing it at compile time means the effort is spent upfront so that readers will 19 | have a fast experience as no requests have to be made on the client. 20 | Doing it at runtime gives more flexibility by moving the work to the client. 21 | This can result in a slow experience for readers though. 22 | It also depends on what framework you use (as in it’s specific to React, Preact, 23 | Vue, etc.) 24 | 25 | ## Embeds at compile time 26 | 27 | You can use [`@remark-embedder/core`][remark-embedder] by doing something like 28 | this: 29 | 30 | ```js path="example.js" 31 | import {compile} from '@mdx-js/mdx' 32 | // Note: `@remark-embedder` is currently using faux-esm. 33 | import fauxRemarkEmbedder from '@remark-embedder/core' 34 | import fauxOembedTransformer from '@remark-embedder/transformer-oembed' 35 | 36 | const remarkEmbedder = fauxRemarkEmbedder.default 37 | const oembedTransformer = fauxOembedTransformer.default 38 | 39 | const code = ` 40 | Check out this video: 41 | 42 | https://www.youtube.com/watch?v=dQw4w9WgXcQ 43 | ` 44 | 45 | console.log( 46 | String( 47 | await compile(code, { 48 | remarkPlugins: [ 49 | [ 50 | // @ts-expect-error: `remarkEmbedder` types are wrong. 51 | remarkEmbedder, 52 | {transformers: [oembedTransformer]} 53 | ] 54 | ] 55 | }) 56 | ) 57 | ) 58 | ``` 59 | 60 |
    61 | Expand equivalent JSX 62 | 63 | ```jsx path="output.jsx" 64 | <> 65 |

    Check out this video:

    66 | 75 | 76 | ``` 77 |
    78 | 79 | ## Embeds at run time 80 | 81 | You can use the React-specific [MDX Embed][mdx-embed] to embed things in MDX. 82 | Here is an example MDX file that uses a specific embed without `@mdx-js/react`: 83 | 84 | ```mdx path="example.mdx" 85 | import {CodePen} from 'mdx-embed' 86 | 87 | Here’s a codepen, and some other blog post text. 88 | 89 | 90 | ``` 91 | 92 |
    93 | Expand equivalent JSX 94 | 95 | ```jsx path="output.jsx" 96 | <> 97 |

    Here’s a codepen, and some other blog post text.

    98 | 99 | 100 | ``` 101 |
    102 | 103 | If you don’t want to use explicit imports in MDX files: 104 | 105 | ```mdx path="example.mdx" 106 | Here’s a codepen, and some other blog post text. 107 | 108 | 109 | ``` 110 | 111 | Then you can either pass all components: 112 | 113 | ```jsx path="example.jsx" 114 | import * as embeds from 'mdx-embed' 115 | import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS. 116 | 117 | console.log() 118 | ``` 119 | 120 | Or, if you’ve installed and configured [`@mdx-js/react`][mdx-react], you can 121 | also use `MDXEmbedProvider`: 122 | 123 | ```jsx path="example.jsx" 124 | import {MDXEmbedProvider} from 'mdx-embed' 125 | import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS. 126 | 127 | console.log( 128 | 129 | 130 | 131 | ) 132 | ``` 133 | 134 | [commonmark]: https://spec.commonmark.org/current/ 135 | 136 | [mdx-embed]: https://mdx-embed.netlify.app/ 137 | 138 | [mdx-react]: /packages/react/ 139 | 140 | [remark-embedder]: https://github.com/remark-embedder/core 141 | -------------------------------------------------------------------------------- /docs/guides/frontmatter.mdx: -------------------------------------------------------------------------------- 1 | export const info = { 2 | author: [ 3 | {github: 'wooorm', name: 'Titus Wormer'} 4 | ], 5 | modified: new Date('2025-01-27'), 6 | published: new Date('2021-10-06') 7 | } 8 | export const navSortSelf = 2 9 | 10 | # Frontmatter 11 | 12 | This guide explores how to support YAML frontmatter in MDX. {/* more */} 13 | MDX supports standard markdown syntax ([CommonMark][]). 14 | That means frontmatter is not supported by default. 15 | 16 | MDX comes with a powerful and dynamic alternative to frontmatter, namely ESM 17 | (`import`/`export`). 18 | These exports: 19 | 20 | ```mdx path="example.mdx" 21 | export const name = 'World' 22 | export const title = 'Hi, ' + name + '!' 23 | 24 | # {title} 25 | ``` 26 | 27 | Can be used like so: 28 | 29 | ```js twoslash path="example.js" 30 | // @filename: types.d.ts 31 | declare module '*.mdx' { 32 | export {MDXContent as default} from 'mdx/types' 33 | export const name: string 34 | export const title: string 35 | } 36 | // @filename: example.js 37 | /// 38 | // ---cut--- 39 | import * as Post from './example.mdx' // Assumes an integration is used to compile MDX -> JS. 40 | 41 | console.log(Post.title) // Prints 'Hi, World!' 42 | ``` 43 | 44 | You might prefer frontmatter though, as it lets you define data that can be 45 | extracted from the file system *before* compiling. 46 | Say our MDX with frontmatter looked like this: 47 | 48 | ```mdx path="example.mdx" 49 | --- 50 | title: Hi, World! 51 | --- 52 | 53 | # Hi, World! 54 | ``` 55 | 56 | Then without compiling or evaluating the metadata can be accessed like so: 57 | 58 | ```js twoslash path="example.js" 59 | /// 60 | // ---cut--- 61 | import {read} from 'to-vfile' 62 | import {matter} from 'vfile-matter' 63 | 64 | const file = await read('example.mdx') 65 | matter(file) 66 | 67 | console.log(file.data.matter) 68 | ``` 69 | 70 | Our compiler, `@mdx-js/mdx`, doesn’t understand YAML frontmatter by default but 71 | it can be enabled by using a remark plugin, 72 | [`remark-frontmatter`][remark-frontmatter]: 73 | 74 | ```js twoslash path="example.js" 75 | // @filename: example.js 76 | /// 77 | // ---cut--- 78 | import fs from 'node:fs/promises' 79 | import {compile} from '@mdx-js/mdx' 80 | import remarkFrontmatter from 'remark-frontmatter' 81 | 82 | const file = await compile(await fs.readFile('example.mdx'), { 83 | remarkPlugins: [remarkFrontmatter] 84 | }) 85 | 86 | console.log(file) 87 | ``` 88 | 89 | Now it “works”. 90 | The frontmatter is not rendered as if it was markdown. 91 | But the data embedded in the frontmatter isn’t available from *inside* the MDX. 92 | What if we wanted that too? 93 | Like so: 94 | 95 | ```mdx path="example.mdx" 96 | --- 97 | title: Hi, World! 98 | --- 99 | 100 | # {title} 101 | ``` 102 | 103 | That’s exactly what the remark plugin 104 | [`remark-mdx-frontmatter`][remark-mdx-frontmatter] does. 105 | 106 | That plugin, like all remark plugins, can be passed as 107 | [`remarkPlugins` in `ProcessorOptions`][processor-options]. 108 | More info on plugins is available in [§ Extending MDX][extend] 109 | 110 | [commonmark]: https://spec.commonmark.org/current/ 111 | 112 | [extend]: /docs/extending-mdx/ 113 | 114 | [processor-options]: /packages/mdx/#processoroptions 115 | 116 | [remark-frontmatter]: https://github.com/remarkjs/remark-frontmatter 117 | 118 | [remark-mdx-frontmatter]: https://github.com/remcohaszing/remark-mdx-frontmatter 119 | -------------------------------------------------------------------------------- /docs/guides/gfm.mdx: -------------------------------------------------------------------------------- 1 | export const info = { 2 | author: [ 3 | {github: 'wooorm', name: 'Titus Wormer'} 4 | ], 5 | modified: new Date('2025-01-27'), 6 | published: new Date('2021-10-06') 7 | } 8 | export const navSortSelf = 1 9 | 10 | # GitHub flavored markdown (GFM) 11 | 12 | This guide explores how to support GFM features such as autolink literals, 13 | footnotes, strikethrough, tables, and task lists. {/* more */} 14 | MDX supports standard markdown syntax ([CommonMark][]). 15 | That means [GitHub flavored markdown (GFM)][gfm] extensions are not supported by 16 | default. 17 | They can be enabled by using a remark plugin: [`remark-gfm`][remark-gfm]. 18 | That plugin, like all remark plugins, can be passed in [`remarkPlugins` in 19 | `ProcessorOptions`][processor-options]. 20 | More info on plugins is available in [§ Extending MDX][extend] 21 | 22 | Say we have an MDX file like this: 23 | 24 | ```mdx path="example.mdx" 25 | # GFM 26 | 27 | ## Autolink literals 28 | 29 | www.example.com, https://example.com, and contact@example.com. 30 | 31 | ## Footnote 32 | 33 | A note[^1] 34 | 35 | [^1]: Big note. 36 | 37 | ## Strikethrough 38 | 39 | ~one~ or ~~two~~ tildes. 40 | 41 | ## Table 42 | 43 | | a | b | c | d | 44 | | - | :- | -: | :-: | 45 | 46 | ## Tasklist 47 | 48 | * [ ] to do 49 | * [x] done 50 | ``` 51 | 52 | The above MDX with GFM can be transformed with the following module: 53 | 54 | ```js twoslash path="example.js" 55 | // @filename: example.js 56 | /// 57 | // ---cut--- 58 | import fs from 'node:fs/promises' 59 | import {compile} from '@mdx-js/mdx' 60 | import remarkGfm from 'remark-gfm' 61 | 62 | console.log( 63 | String( 64 | await compile(await fs.readFile('example.mdx'), {remarkPlugins: [remarkGfm]}) 65 | ) 66 | ) 67 | ``` 68 | 69 |
    70 | Expand equivalent JSX 71 | 72 | ```jsx path="output.jsx" 73 | <> 74 |

    GFM

    75 |

    Autolink literals

    76 |

    77 | www.example.com,{' '} 78 | https://example.com, and{' '} 79 | contact@example.com. 80 |

    81 |

    Footnote

    82 |

    83 | A note 84 | 85 | 91 | 1 92 | 93 | 94 |

    95 |

    Strikethrough

    96 |

    97 | one or two tildes. 98 |

    99 |

    Table

    100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 |
    abcd
    110 |

    Tasklist

    111 |
      112 |
    • 113 | to do 114 |
    • 115 |
    • 116 | 117 | done 118 |
    • 119 |
    120 |
    121 |

    122 | Footnotes 123 |

    124 |
      125 |
    1. 126 |

      127 | Big note.{' '} 128 | 134 | ↩ 135 | 136 |

      137 |
    2. 138 |
    139 |
    140 | 141 | ``` 142 |
    143 | 144 | [commonmark]: https://spec.commonmark.org/current/ 145 | 146 | [extend]: /docs/extending-mdx/ 147 | 148 | [gfm]: https://github.github.com/gfm/ 149 | 150 | [processor-options]: /packages/mdx/#processoroptions 151 | 152 | [remark-gfm]: https://github.com/remarkjs/remark-gfm 153 | -------------------------------------------------------------------------------- /docs/guides/index.mdx: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * @import {Item} from '../_component/sort.js' 4 | */ 5 | 6 | /** 7 | * @typedef Props 8 | * @property {Item} navigationTree 9 | */ 10 | } 11 | 12 | import assert from 'node:assert/strict' 13 | import {NavigationGroup} from '../_component/nav.jsx' 14 | 15 | export const info = { 16 | author: [{name: 'MDX Contributors'}], 17 | modified: new Date('2024-07-04'), 18 | published: new Date('2021-11-01') 19 | } 20 | export const navSortSelf = 2 21 | 22 | # Guides 23 | 24 | These guides explain how to accomplish several common use cases and patterns 25 | around MDX. 26 | 27 | { 28 | (function () { 29 | const navigationTree = props.navigationTree 30 | const category = navigationTree.children.find(function (item) { 31 | return item.name === '/guides/' 32 | }) 33 | assert(category) 34 | 35 | return ( 36 | 39 | ) 40 | })() 41 | } 42 | -------------------------------------------------------------------------------- /docs/guides/injecting-components.mdx: -------------------------------------------------------------------------------- 1 | export const info = { 2 | author: [ 3 | {github: 'wooorm', name: 'Titus Wormer'} 4 | ], 5 | modified: new Date('2024-07-04'), 6 | published: new Date('2023-10-24') 7 | } 8 | export const navSortSelf = 7 9 | 10 | # Injecting components 11 | 12 | This guide shows how to inject arbitrary components into MDX when it 13 | runs. {/* more */} 14 | It shows how the underlying features used by our providers (`@mdx-js/react`, 15 | `@mdx-js/preact`) and the [`mdx-components.tsx`][next-mdx-components] file 16 | supported by Next.js work, 17 | and how you can take advantage of that functionality yourself. 18 | 19 | In many cases you do not need this, 20 | as you can pass components to MDX: 21 | 22 | ```mdx path="example.mdx" 23 | # Hello ** 24 | ``` 25 | 26 | You can pass `Planet` and say a component used instead of the `h1`: 27 | 28 | ```jsx twoslash path="example.jsx" 29 | // @filename: types.d.ts 30 | import type {} from 'mdx' 31 | // @filename: example.jsx 32 | /// 33 | /* @jsxImportSource react */ 34 | // ---cut--- 35 | import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS. 36 | 37 | console.log( 38 | 45 | } 46 | }} 47 | /> 48 | ) 49 | ``` 50 | 51 | When you find yourself passing that `components` prop around a lot, 52 | you might want to look at an alternative. 53 | You might reach for our context based providers (`@mdx-js/react`, 54 | `@mdx-js/preact`), 55 | but context has performance downsides and context doesn’t always work (such as 56 | in RSC). 57 | 58 | But first, 59 | how does component passing work? 60 | That can be illustrated by looking at the code generated by MDX for the above 61 | `example.mdx`. 62 | Here is a diff that shows what the example normally compiles to and what 63 | changes when `providerImportSource: 'xxx'` is passed: 64 | 65 | ```diff 66 | @@ -1,7 +1,13 @@ 67 | import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' 68 | +import {useMDXComponents as _provideComponents} from 'xxx' 69 | 70 | function _createMdxContent(props) { 71 | - const _components = {em: 'em', h1: 'h1', ...props.components} 72 | + const _components = { 73 | + em: 'em', 74 | + h1: 'h1', 75 | + ..._provideComponents(), 76 | + ...props.components 77 | + } 78 | const {Planet} = _components 79 | if (!Planet) _missingMdxReference('Planet', true) 80 | return _jsxs(_components.h1, { 81 | @@ -10,7 +16,7 @@ function _createMdxContent(props) { 82 | } 83 | 84 | export default function MDXContent(props = {}) { 85 | - const {wrapper: MDXLayout} = props.components || {} 86 | + const {wrapper: MDXLayout} = {..._provideComponents(), ...props.components} 87 | return MDXLayout 88 | ? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {...props})}) 89 | : _createMdxContent(props) 90 | ``` 91 | 92 | Observe that components have defaults (such as that `h1` will use `'h1'`) and 93 | that components are taken from `props.components`. 94 | What changes is an added call to `_provideComponents`, 95 | which refers to an `useMDXComponents` export from the module we specified 96 | (`xxx`). 97 | 98 | We can use this interface to inject components from a file. 99 | In that file, 100 | we need a `useMDXComponents` function that returns our components. 101 | 102 | ```jsx twoslash path="mdx-components.js" 103 | // @filename: mdx-components.jsx 104 | /* @jsxImportSource react */ 105 | // ---cut--- 106 | /** 107 | * @import {MDXComponents} from 'mdx/types.js' 108 | */ 109 | 110 | /** @returns {MDXComponents} */ 111 | export function useMDXComponents() { 112 | return { 113 | Planet() { 114 | return 'Pluto' 115 | }, 116 | h1(properties) { 117 | return

    118 | } 119 | } 120 | } 121 | ``` 122 | 123 | And now passing a file path or URL to that file as `providerImportSource`, 124 | such as with `import.meta.resolve('./mdx-components.js')`: 125 | 126 | ```diff 127 | @@ -1,5 +1,5 @@ 128 | import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime' 129 | -import {useMDXComponents as _provideComponents} from 'xxx' 130 | +import {useMDXComponents as _provideComponents} from 'file:///Users/tilde/…/mdx-components.js' 131 | ``` 132 | 133 | Now our locally defined components will be used in all MDX files! 134 | 135 | [next-mdx-components]: https://nextjs.org/docs/pages/building-your-application/configuring/mdx 136 | -------------------------------------------------------------------------------- /docs/guides/math.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'wooorm', name: 'Titus Wormer'} 6 | ], 7 | modified: new Date('2025-01-27'), 8 | published: new Date('2021-10-06') 9 | } 10 | export const navSortSelf = 3 11 | 12 | # Math 13 | 14 | This guide explores how to support math (LaTeX) in MDX. {/* more */} 15 | MDX supports standard markdown syntax ([CommonMark][]). 16 | That means math is not supported by default. 17 | Math can be enabled by using a remark plugin: [`remark-math`][remark-math], 18 | combined with a rehype plugin: either 19 | [`rehype-katex`][rehype-katex] (KaTeX) or [`rehype-mathjax`][rehype-mathjax] 20 | (MathJax). 21 | Like other remark and rehype plugins, they can be passed in [`remarkPlugins` 22 | and `rehypePlugins`, respectively, in `ProcessorOptions`][processor-options]. 23 | More info on plugins is available in [§ Extending MDX][extend] 24 | 25 | Say we have an MDX file like this: 26 | 27 | ```mdx path="example.mdx" 28 | # $$\sqrt{a^2 + b^2}$$ 29 | ``` 30 | 31 | The above MDX with math can be transformed with the following module: 32 | 33 | ```js twoslash path="example.js" 34 | // @filename: example.js 35 | /// 36 | // ---cut--- 37 | import fs from 'node:fs/promises' 38 | import {compile} from '@mdx-js/mdx' 39 | import rehypeKatex from 'rehype-katex' 40 | import remarkMath from 'remark-math' 41 | 42 | console.log( 43 | String( 44 | await compile(await fs.readFile('example.mdx'), { 45 | rehypePlugins: [rehypeKatex], 46 | remarkPlugins: [remarkMath] 47 | }) 48 | ) 49 | ) 50 | ``` 51 | 52 |
    53 | Expand equivalent JSX 54 | 55 | ```jsx path="output.jsx" 56 | <> 57 |

    58 | 59 | 60 | 61 | 62 | 65 | 66 |

    67 | 68 | ``` 69 |
    70 | 71 | 72 | **Important**: if you chose `rehype-katex`, you should also use `katex.css` 73 | somewhere on the page to style math properly. 74 | At the time of writing, the last version is: 75 | 76 | ```html 77 | 78 | 79 | ``` 80 | 81 | To get the latest link to the stylesheet, go to [`katex docs`][katex-browser]. 82 | 83 | {/* to do: once in a while, get the latest. */} 84 | 85 | 86 | 87 | **Note:** see also 88 | [`remark-mdx-math-enhanced`](https://github.com/goodproblems/remark-mdx-math-enhanced), 89 | which you can use to support JavaScript expressions inside of math (such as to 90 | access properties or to make calculations) 91 | 92 | 93 | [commonmark]: https://spec.commonmark.org/current/ 94 | 95 | [extend]: /docs/extending-mdx/ 96 | 97 | [katex-browser]: https://katex.org/docs/browser#loading-as-global 98 | 99 | [processor-options]: /packages/mdx/#processoroptions 100 | 101 | [rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex 102 | 103 | [rehype-mathjax]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax 104 | 105 | [remark-math]: https://github.com/remarkjs/remark-math/tree/main/packages/remark-math 106 | -------------------------------------------------------------------------------- /docs/guides/mdx-on-demand.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'wooorm', name: 'Titus Wormer'} 6 | ], 7 | modified: new Date('2025-01-27'), 8 | published: new Date('2021-11-13') 9 | } 10 | export const navSortSelf = 6 11 | 12 | # MDX on demand 13 | 14 | This guide shows how to use `@mdx-js/mdx` to compile MDX on the server and run 15 | the result on clients. {/* more */} 16 | Some frameworks, such as Next.js and Remix, make it easy to split work between 17 | servers and clients. 18 | Using that it’s possible to for example do most of the work on demand on the 19 | server instead of at build time, then pass the resulting data to clients, where 20 | they finally use it. 21 | 22 | This is similar to what people sometimes use [`mdx-bundler`][mdx-bundler] or 23 | [`next-mdx-remote`][next-mdx-remote] for, but MDX also supports it. 24 | 25 | ## Quick example 26 | 27 | On the server: 28 | 29 | ```js twoslash path="server.js" 30 | import {compile} from '@mdx-js/mdx' 31 | 32 | const code = String(await compile('# hi', { 33 | outputFormat: 'function-body', 34 | /* …otherOptions */ 35 | })) 36 | // To do: send `code` to the client somehow. 37 | ``` 38 | 39 | On the client: 40 | 41 | ```js twoslash path="client.js" 42 | import {run} from '@mdx-js/mdx' 43 | import * as runtime from 'react/jsx-runtime' 44 | 45 | const code = '' // To do: get `code` from server somehow. 46 | 47 | const {default: Content} = await run(code, {...runtime, baseUrl: import.meta.url}) 48 | ``` 49 | 50 | `Content` is now an `MDXContent` component that you can use like normal in your 51 | framework (see [§ Using MDX][use]). 52 | 53 | More information is available in the API docs of `@mdx-js/mdx` for 54 | [`compile`][compile] and [`run`][run]. 55 | For other use cases, you can also use [`evaluate`][eval], which both compiles 56 | and runs in one. 57 | 58 | 59 | **Note**: MDX is not a bundler (esbuild, webpack, and Rollup are bundlers): 60 | you can’t import other code from the server within the string of MDX and get a 61 | nicely minified bundle out or so. 62 | 63 | 64 | ## Next.js example 65 | 66 | Some frameworks let you write the server and client code in one file, such as 67 | Next. 68 | 69 | ```js twoslash path="pages/hello.js" 70 | /** 71 | * @import {MDXModule} from 'mdx/types.js' 72 | * @import {Dispatch, ReactElement, SetStateAction} from 'react' 73 | */ 74 | 75 | import {compile, run} from '@mdx-js/mdx' 76 | import {Fragment, useEffect, useState} from 'react' 77 | import * as runtime from 'react/jsx-runtime' 78 | 79 | /** 80 | * @param {{code: string}} props 81 | * @returns {ReactElement} 82 | */ 83 | export default function Page({code}) { 84 | /** @type {[MDXModule | undefined, Dispatch>]} */ 85 | const [mdxModule, setMdxModule] = useState() 86 | const Content = mdxModule ? mdxModule.default : Fragment 87 | 88 | useEffect( 89 | function () { 90 | ;(async function () { 91 | setMdxModule(await run(code, {...runtime, baseUrl: import.meta.url})) 92 | })() 93 | }, 94 | [code] 95 | ) 96 | 97 | return 98 | } 99 | 100 | export async function getStaticProps() { 101 | const code = String( 102 | await compile('# hi', { 103 | outputFormat: 'function-body' 104 | /* …otherOptions */ 105 | }) 106 | ) 107 | return {props: {code}} 108 | } 109 | ``` 110 | 111 | [compile]: /packages/mdx/#compilefile-options 112 | 113 | [eval]: /packages/mdx/#evaluatefile-options 114 | 115 | [mdx-bundler]: https://github.com/kentcdodds/mdx-bundler 116 | 117 | [next-mdx-remote]: https://github.com/hashicorp/next-mdx-remote 118 | 119 | [run]: /packages/mdx/#runcode-options 120 | 121 | [use]: /docs/using-mdx/ 122 | -------------------------------------------------------------------------------- /docs/index.mdx: -------------------------------------------------------------------------------- 1 | import {Chart} from './_component/snowfall.jsx' 2 | 3 | export {Home as default} from './_component/home.jsx' 4 | export const info = { 5 | author: [ 6 | {github: 'johno', name: 'John Otander'}, 7 | {github: 'wooorm', name: 'Titus Wormer'} 8 | ], 9 | modified: new Date('2025-01-27'), 10 | published: new Date('2017-12-23'), 11 | schemaOrg: { 12 | "@context": "https://schema.org", 13 | "@type": "SoftwareApplication", 14 | "additionalType": "ComputerLanguage", 15 | "applicationCategory": "DeveloperApplication", 16 | "description": "an authorable format for writing JSX in markdown documents", 17 | "name": "MDX", 18 | "offers": { 19 | "@type": "Offer", 20 | "price": "0.00", 21 | "priceCurrency": "USD" 22 | }, 23 | "operatingSystem": "Windows, MacOS, Linux", 24 | "sameAs": [ 25 | "https://www.wikidata.org/wiki/Q95971592", 26 | "https://www.wikidata.org/wiki/Q27966906", 27 | "https://www.wikidata.org/wiki/Q95961071", 28 | "https://en.wikipedia.org/wiki/MDX_(markup_language)", 29 | "https://github.com/mdx-js/mdx" 30 | ], 31 | "url": "https://mdxjs.com" 32 | } 33 | } 34 | export const year = 2023 35 | 36 | {/* lint disable heading-style */} 37 | 38 | Markdown for the\ 39 | **component era** 40 | ================= 41 | 42 | MDX lets you use JSX in your markdown content. 43 | You can import components, such as interactive charts or alerts, and embed them 44 | within your content. 45 | This makes writing long-form content with components a blast. {/* more */} 46 | 🚀 47 | [Continue reading »][what] 48 | 49 |
    50 | ## New: MDX 3! 51 | 52 | A small major this time, nothing big, which is also nice sometimes! 53 | This mainly drops support for old Node (use 16 or later), adds modern ES2024 54 | support in MDX, supports `await` in MDX (if your framework does too), and 55 | removes several deprecated options. 56 | 57 | [Continue reading »][v3] 58 |
    59 | 60 | ## What does MDX do? 61 | 62 |
    63 |
    64 | You write markdown with embedded components through JSX: 65 | 66 | ```mdx path="example.mdx" 67 | import {Chart} from './snowfall.js' 68 | export const year = 2023 69 | 70 | # Last year’s snowfall 71 | 72 | In {year}, the snowfall was above average. 73 | It was followed by a warm spring which caused 74 | flood conditions in many of the nearby rivers. 75 | 76 | 77 | ``` 78 |
    79 | 80 |
    81 | It gets compiled to JavaScript that you can use in any framework that 82 | supports JSX: 83 | 84 | {/* lint disable no-multiple-toplevel-headings */} 85 | 86 |
    87 | # Last year’s snowfall 88 | 89 | In {year}, the snowfall was above average. 90 | It was followed by a warm spring which caused flood conditions in many of 91 | the nearby rivers. 92 | 93 | 94 |
    95 | 96 | {/* lint enable no-multiple-toplevel-headings */} 97 |
    98 |
    99 | 100 | We made an interactive playground where you can try MDX out and see what it 101 | turns into. 102 | [Play »][playground] 103 | 104 | ## Get started 105 | 106 | There are integrations for most bundlers, frameworks, and editors. 107 | Whether you build with Docusaurus, Next.js, or Vite. 108 | You prefer Rollup, esbuild, or webpack. 109 | You’re using React, Preact, or Vue. 110 | [Get started »][getting-started] 111 | 112 | ## MDX in short 113 | 114 |
    115 | * ❤️ **Powerful**: MDX blends markdown and JSX syntax to fit perfectly in 116 | JSX-based projects 117 | * 💻 **Everything is a component**: Use existing components in your 118 | MDX and import other MDX files as components 119 | * 🔧 **Customizable**: Decide which component is rendered for each markdown 120 | construct (`{h1: MyHeading}`) 121 | * 📚 **Markdown-based**: The simplicity and elegance of markdown remains, 122 | you use JSX only when you want to 123 | * 🔥 **Blazingly blazing fast**: MDX has no runtime, all compilation occurs 124 | during the build stage 125 |
    126 | 127 | > lol mdx is so good 128 | > 129 | > — **@dan\_abramov** 130 | 131 | [getting-started]: /docs/getting-started/ 132 | 133 | [playground]: /playground/ 134 | 135 | [v3]: /blog/v3/ 136 | 137 | [what]: /docs/what-is-mdx/ 138 | -------------------------------------------------------------------------------- /docs/migrating/v1.mdx: -------------------------------------------------------------------------------- 1 | import {Note} from '../_component/note.jsx' 2 | 3 | export const info = { 4 | author: [ 5 | {github: 'johno', name: 'John Otander'} 6 | ], 7 | modified: new Date('2025-01-27'), 8 | published: new Date('2019-04-04') 9 | } 10 | export const navExclude = true 11 | 12 | 13 | **Note**: This is an old migration guide. 14 | See [§ Migrating from v1 to v2](/migrating/v2/). 15 | The below is kept as is for historical purposes. 16 | 17 | 18 | # Migrating from v0 to v1 19 | 20 | Unfortunately, we’ve had to introduce a few breaking changes, so we’ve written a 21 | migration guide. 22 | In order to ensure as seamless of an upgrade as possible we plan on supporting 23 | v0 for the next 12 months so there’s not a huge rush to update (though we’d 24 | love for you to ASAP) 📆. 25 | 26 | ## ⚠️ Breaking changes 27 | 28 | * [🚨 `@mdx-js/tag` is replaced by `@mdx-js/react` and an `mdx` 29 | pragma](#pragma) 🚨 30 | * [MDXProvider now merges component contexts when nested](#mdxprovider) 31 | * [React support now requires `>= 16.8` in `@mdx-js/react`](#react) 32 | 33 | ## Pragma 34 | 35 | For v1 you need to remove `@mdx-js/tag` and replace it with `@mdx-js/react`: 36 | 37 | ```sh 38 | yarn remove @mdx-js/tag 39 | yarn add @mdx-js/react 40 | ``` 41 | 42 | ### What’s different? 43 | 44 | The MDXTag implementation has been removed with a custom pragma implementation 45 | inspired by 46 | [Emotion](https://emotion.sh/docs/css-prop#jsx-pragma). 47 | This ensures that transpiled JSX is more readable and that JSX blocks use the 48 | same component as its markdown counterpart. 49 | It also allows MDXProvider to provide global component scope like a `Youtube` 50 | component. 51 | 52 | The pragma implementation will also cause JSX HTML elements to be rendered with 53 | the component mapping passed to MDXProvider. 54 | So, the following will result in two identically rendered `h1`s: 55 | 56 | ```mdx 57 | # Hello, world! 58 | 59 |

    Hello, world!

    60 | ``` 61 | 62 | [See the blog post for further reading](/blog/custom-pragma/) 63 | 64 | ## MDXProvider 65 | 66 | This shouldn’t affect most usecases, however if you’re nesting component 67 | contexts and rely on them not being merged you will have to use the functional 68 | form which allows you to customize the merge. 69 | By ignoring outer context components and returning a new component mapping, you 70 | will restore the old behavior: 71 | 72 | ```tsx 73 | 74 | newComponents}> 75 | {children} 76 | 77 | 78 | ``` 79 | 80 | ## React 81 | 82 | Before upgrading to `@mdx-js/mdx@1`, update your website/application to 83 | `react@16.8 react-dom@16.8` and ensure it works as expected. 84 | Then upgrade to v1. 85 | -------------------------------------------------------------------------------- /docs/migrating/v3.mdx: -------------------------------------------------------------------------------- 1 | export const info = { 2 | author: [ 3 | {github: 'wooorm', name: 'Titus Wormer'} 4 | ], 5 | modified: new Date('2023-10-24'), 6 | published: new Date('2023-10-24') 7 | } 8 | export const navExclude = true 9 | 10 | # Migrating from v2 to v3 11 | 12 | A couple small changes this time around. 13 | For most folks, updating Node.js and plugins is all that’s needed! 14 | 15 | ## Contents 16 | 17 | * [Update Node.js](#update-nodejs) 18 | * [Update plugins](#update-plugins) 19 | * [Pass `baseUrl` to `evaluate`, `run`](#pass-baseurl-to-evaluate-run) 20 | * [Use the automatic JSX runtime](#use-the-automatic-jsx-runtime) 21 | * [Replace `MDXContext`, `withMDXComponents` with `useMDXComponents`](#replace-mdxcontext-withmdxcomponents-with-usemdxcomponents) 22 | * [Replace `@mdx-js/register` with `@mdx-js/node-loader`](#replace-mdx-jsregister-with-mdx-jsnode-loader) 23 | 24 | ## Update Node.js 25 | 26 | If you’re still on old Node, it’s time to update. 27 | Use at least Node 16. 28 | 29 | ## Update plugins 30 | 31 | If you use rehype and remark plugins: update. 32 | 33 | Most of them remain working between versions. 34 | Particularly if you use TypeScript, there was a breaking internal change in the 35 | types, which is now supported here. 36 | There were also some small internal changes in how the parser works, which 37 | affect `remark-gfm` and `remark-math`. 38 | 39 | ## Pass `baseUrl` to `evaluate`, `run` 40 | 41 | If you use `evaluate` or `run` (or `outputFormat: 'function-body'`), you should 42 | pass the `baseUrl` option. 43 | Likely set to `import.meta.url`. 44 | 45 | If MDX content uses `export` statements, `import` expressions/statements, 46 | or `import.meta.url`, we compile to code that works, but it needs to know 47 | where the code runs: you need to pass where you are. 48 | 49 | You will get a runtime error if these features are used in MDX without 50 | `baseUrl`. 51 | 52 | If you passed the `useDynamicImport` option before, remove it, the behavior 53 | is now the default. 54 | 55 | ```tsx 56 | import * as runtime from 'react/jsx-runtime' 57 | 58 | const result = await run('# hi', {...runtime, baseUrl: import.meta.url}) 59 | ``` 60 | 61 | ## Use the automatic JSX runtime 62 | 63 | If you use the classic runtime, switch to the automatic runtime. 64 | Classic is still supported but you will now see a warning if you use the 65 | classic JSX runtime. 66 | 67 | The classic runtime can still be nice in other places, but it causes potential 68 | problems in MDX. 69 | If you specify `jsxRuntime: 'classic'` (and `pragma`, `pragmaFrag`, 70 | `pragmaImportSource`), consider switching to `automatic`. 71 | All major frameworks support the automatic runtime. 72 | Support for the classic runtime will likely be removed next major. 73 | 74 | ## Replace `MDXContext`, `withMDXComponents` with `useMDXComponents` 75 | 76 | If you use the deprecated symbols `MDXContext` and `withMDXComponents`, use 77 | `useMDXComponents` instead. 78 | 79 | Reminder: you probably don’t need the context based `@mdx-js/react` or 80 | `@mdx-js/preact` packages. 81 | 82 | ## Replace `@mdx-js/register` with `@mdx-js/node-loader` 83 | 84 | If you use the deprecated package `@mdx-js/register`, use `@mdx-js/node-loader` instead. 85 | -------------------------------------------------------------------------------- /docs/packages/esbuild.md: -------------------------------------------------------------------------------- 1 | ../../packages/esbuild/readme.md -------------------------------------------------------------------------------- /docs/packages/index.mdx: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | * @import {Item} from '../_component/sort.js' 4 | */ 5 | 6 | /** 7 | * @typedef Props 8 | * @property {Item} navigationTree 9 | */ 10 | } 11 | 12 | import assert from 'node:assert/strict' 13 | import {NavigationGroup} from '../_component/nav.jsx' 14 | 15 | export const info = { 16 | author: [{name: 'MDX Contributors'}], 17 | modified: new Date('2024-07-04'), 18 | published: new Date('2021-11-01') 19 | } 20 | export const navSortSelf = 3 21 | 22 | # Packages 23 | 24 | This index lists the packages that are maintained in our monorepo. 25 | They include `@mdx-js/mdx`, which is our core compiler; `remark-mdx`, which is 26 | the remark plugin to support the MDX syntax; and several integrations with 27 | bundlers and frontend frameworks. 28 | 29 | { 30 | (function () { 31 | const navigationTree = props.navigationTree 32 | const category = navigationTree.children.find(function (item) { 33 | return item.name === '/packages/' 34 | }) 35 | assert(category) 36 | 37 | return ( 38 | 41 | ) 42 | })() 43 | } 44 | -------------------------------------------------------------------------------- /docs/packages/loader.md: -------------------------------------------------------------------------------- 1 | ../../packages/loader/readme.md -------------------------------------------------------------------------------- /docs/packages/mdx.md: -------------------------------------------------------------------------------- 1 | ../../packages/mdx/readme.md -------------------------------------------------------------------------------- /docs/packages/node-loader.md: -------------------------------------------------------------------------------- 1 | ../../packages/node-loader/readme.md -------------------------------------------------------------------------------- /docs/packages/preact.md: -------------------------------------------------------------------------------- 1 | ../../packages/preact/readme.md -------------------------------------------------------------------------------- /docs/packages/react.md: -------------------------------------------------------------------------------- 1 | ../../packages/react/readme.md -------------------------------------------------------------------------------- /docs/packages/remark-mdx.md: -------------------------------------------------------------------------------- 1 | ../../packages/remark-mdx/readme.md -------------------------------------------------------------------------------- /docs/packages/rollup.md: -------------------------------------------------------------------------------- 1 | ../../packages/rollup/readme.md -------------------------------------------------------------------------------- /docs/packages/vue.md: -------------------------------------------------------------------------------- 1 | ../../packages/vue/readme.md -------------------------------------------------------------------------------- /docs/playground.mdx: -------------------------------------------------------------------------------- 1 | export const info = { 2 | author: [ 3 | {github: 'johno', name: 'John Otander'}, 4 | {github: 'wooorm', name: 'Titus Wormer'} 5 | ], 6 | modified: new Date('2023-12-24'), 7 | published: new Date('2021-09-13') 8 | } 9 | export const navSortSelf = 5 10 | 11 | # Playground 12 | 13 | Here you can play with the MDX format. 14 | Write some MDX to find out what it turns into. {/* more */} 15 | You can see the rendered result, the generated code, and the intermediary 16 | ASTs. 17 | This can be helpful for debugging or exploring. 18 | To read about how the MDX format works, we recommend that you start with 19 | [§ What is MDX][what]. 20 | 21 |
    22 |
    23 |
    24 | 25 | [what]: /docs/what-is-mdx/ 26 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Compositor and Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/esbuild/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('./lib/index.js').Options} Options 3 | */ 4 | 5 | export {esbuild as default} from './lib/index.js' 6 | -------------------------------------------------------------------------------- /packages/esbuild/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Titus Wormer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/esbuild/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mdx-js/esbuild", 3 | "version": "3.1.0", 4 | "description": "esbuild plugin for MDX", 5 | "license": "MIT", 6 | "keywords": [ 7 | "esbuild", 8 | "jsx", 9 | "markdown", 10 | "mdx", 11 | "preact", 12 | "react", 13 | "remark", 14 | "vue" 15 | ], 16 | "homepage": "https://mdxjs.com", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/mdx-js/mdx", 20 | "directory": "packages/esbuild/" 21 | }, 22 | "bugs": "https://github.com/mdx-js/mdx/issues", 23 | "funding": { 24 | "type": "opencollective", 25 | "url": "https://opencollective.com/unified" 26 | }, 27 | "author": "Titus Wormer (https://wooorm.com)", 28 | "contributors": [ 29 | "Titus Wormer (https://wooorm.com)" 30 | ], 31 | "type": "module", 32 | "sideEffects": false, 33 | "exports": "./index.js", 34 | "files": [ 35 | "lib/", 36 | "index.d.ts.map", 37 | "index.d.ts", 38 | "index.js" 39 | ], 40 | "dependencies": { 41 | "@mdx-js/mdx": "^3.0.0", 42 | "@types/unist": "^3.0.0", 43 | "source-map": "^0.7.0", 44 | "vfile": "^6.0.0", 45 | "vfile-message": "^4.0.0" 46 | }, 47 | "peerDependencies": { 48 | "esbuild": ">=0.14.0" 49 | }, 50 | "scripts": { 51 | "test": "npm run test-coverage", 52 | "test-api": "node --conditions development --enable-source-maps test/index.js", 53 | "test-coverage": "c8 --100 --reporter lcov npm run test-api" 54 | }, 55 | "xo": { 56 | "prettier": true, 57 | "rules": { 58 | "n/file-extension-in-import": "off" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/esbuild/readme.md: -------------------------------------------------------------------------------- 1 | # `@mdx-js/esbuild` 2 | 3 | [![Build][build-badge]][build] 4 | [![Coverage][coverage-badge]][coverage] 5 | [![Downloads][downloads-badge]][downloads] 6 | [![Sponsors][sponsors-badge]][collective] 7 | [![Backers][backers-badge]][collective] 8 | [![Chat][chat-badge]][chat] 9 | 10 | esbuild plugin for MDX. 11 | 12 | 13 | 14 | ## Contents 15 | 16 | * [What is this?](#what-is-this) 17 | * [When should I use this?](#when-should-i-use-this) 18 | * [Install](#install) 19 | * [Use](#use) 20 | * [API](#api) 21 | * [`mdx(options?)`](#mdxoptions) 22 | * [`Options`](#options) 23 | * [Types](#types) 24 | * [Compatibility](#compatibility) 25 | * [Security](#security) 26 | * [Contribute](#contribute) 27 | * [License](#license) 28 | 29 | ## What is this? 30 | 31 | This package is an esbuild plugin to support MDX. 32 | 33 | ## When should I use this? 34 | 35 | This integration is useful if you’re using [esbuild][] (or another tool that 36 | uses esbuild). 37 | 38 | If you want to evaluate MDX code then the lower-level compiler (`@mdx-js/mdx`) 39 | can be used. 40 | to support nonstandard JSX runtime (such as Vue), `@mdx-js/mdx` can also be 41 | used, or our webpack loader (`@mdx-js/loader`) or Rollup plugin 42 | (`@mdx-js/rollup`). 43 | 44 | ## Install 45 | 46 | This package is [ESM only][esm]. 47 | In Node.js (version 16+), install with [npm][]: 48 | 49 | ```sh 50 | npm install @mdx-js/esbuild 51 | ``` 52 | 53 | ## Use 54 | 55 | Do something like this with the esbuild API: 56 | 57 | ```tsx 58 | import mdx from '@mdx-js/esbuild' 59 | import esbuild from 'esbuild' 60 | 61 | await esbuild.build({ 62 | // Replace `index.js` with your entry point that imports MDX files: 63 | entryPoints: ['index.js'], 64 | format: 'esm', 65 | outfile: 'output.js', 66 | plugins: [mdx({/* jsxImportSource: …, otherOptions… */})] 67 | }) 68 | ``` 69 | 70 | ## API 71 | 72 | This package exports no identifiers. 73 | The default export is [`mdx`][api-mdx]. 74 | 75 | ### `mdx(options?)` 76 | 77 | Create an esbuild plugin to compile MDX to JS. 78 | 79 | esbuild takes care of turning modern JavaScript features into syntax that works 80 | wherever you want it to. 81 | With other integrations you might need to use Babel for this, but with 82 | esbuild that’s not needed. 83 | See esbuild’s docs for more info. 84 | 85 | ###### Parameters 86 | 87 | * `options` ([`Options`][api-options], optional) 88 | — configuration 89 | 90 | ###### Returns 91 | 92 | ESBuild plugin ([`Plugin`][esbuild-plugin] from `esbuild`). 93 | 94 | ### `Options` 95 | 96 | Configuration (TypeScript type). 97 | 98 | Options are the same as [`CompileOptions` from `@mdx-js/mdx`][compile-options]. 99 | 100 | ## Types 101 | 102 | This package is fully typed with [TypeScript][]. 103 | It exports the additional type [`Options`][api-options]. 104 | See [§ Types][types] on our website for information. 105 | 106 | ## Compatibility 107 | 108 | Projects maintained by the unified collective are compatible with maintained 109 | versions of Node.js. 110 | 111 | When we cut a new major release, we drop support for unmaintained versions of 112 | Node. 113 | This means we try to keep the current release line, `@mdx-js/esbuild@^3`, 114 | compatible with Node.js 16. 115 | 116 | ## Security 117 | 118 | See [§ Security][security] on our website for information. 119 | 120 | ## Contribute 121 | 122 | See [§ Contribute][contribute] on our website for ways to get started. 123 | See [§ Support][support] for ways to get help. 124 | 125 | This project has a [code of conduct][coc]. 126 | By interacting with this repository, organization, or community you agree to 127 | abide by its terms. 128 | 129 | ## License 130 | 131 | [MIT][] © [Titus Wormer][author] 132 | 133 | [api-mdx]: #mdxoptions 134 | 135 | [api-options]: #options 136 | 137 | [author]: https://wooorm.com 138 | 139 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg 140 | 141 | [build]: https://github.com/mdx-js/mdx/actions 142 | 143 | [build-badge]: https://github.com/mdx-js/mdx/workflows/main/badge.svg 144 | 145 | [chat]: https://github.com/mdx-js/mdx/discussions 146 | 147 | [chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg 148 | 149 | [coc]: https://github.com/mdx-js/.github/blob/main/code-of-conduct.md 150 | 151 | [collective]: https://opencollective.com/unified 152 | 153 | [compile-options]: https://mdxjs.com/packages/mdx/#compileoptions 154 | 155 | [contribute]: https://mdxjs.com/community/contribute/ 156 | 157 | [coverage]: https://codecov.io/github/mdx-js/mdx 158 | 159 | [coverage-badge]: https://img.shields.io/codecov/c/github/mdx-js/mdx/main.svg 160 | 161 | [downloads]: https://www.npmjs.com/package/@mdx-js/esbuild 162 | 163 | [downloads-badge]: https://img.shields.io/npm/dm/@mdx-js/esbuild.svg 164 | 165 | [esbuild]: https://esbuild.github.io 166 | 167 | [esbuild-plugin]: https://esbuild.github.io/plugins/ 168 | 169 | [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c 170 | 171 | [mit]: https://github.com/mdx-js/mdx/blob/main/packages/esbuild/license 172 | 173 | [npm]: https://docs.npmjs.com/cli/install 174 | 175 | [security]: https://mdxjs.com/getting-started/#security 176 | 177 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg 178 | 179 | [support]: https://mdxjs.com/community/support/ 180 | 181 | [types]: https://mdxjs.com/getting-started/#types 182 | 183 | [typescript]: https://www.typescriptlang.org 184 | -------------------------------------------------------------------------------- /packages/esbuild/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/loader/index.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {LoaderContext} from 'webpack' 3 | * @import {Options as Options_} from './lib/index.js' with {'resolution-mode': 'import'} 4 | */ 5 | 6 | /** 7 | * @typedef {Options_} Options 8 | */ 9 | 10 | 'use strict' 11 | 12 | // Note: we can’t export immediately, as TS generates broken types. 13 | // See: mdx-js/mdx#2386. 14 | module.exports = loader 15 | 16 | /** 17 | * Webpack loader 18 | * 19 | * @todo once webpack supports ESM loaders, remove this wrapper. 20 | * 21 | * @this {LoaderContext} 22 | * Context. 23 | * @param {string} code 24 | * Code. 25 | * @returns {undefined} 26 | * Nothing. 27 | */ 28 | function loader(code) { 29 | const callback = this.async() 30 | // Note that `import()` caches, so this should be fast enough. 31 | import('./lib/index.js').then((module) => { 32 | return module.loader.call(this, code, callback) 33 | }, callback) 34 | } 35 | -------------------------------------------------------------------------------- /packages/loader/lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {CompileOptions} from '@mdx-js/mdx' 3 | * @import {Compatible, VFile} from 'vfile' 4 | * @import {VFileMessage} from 'vfile-message' 5 | * @import {Compiler as WebpackCompiler, LoaderContext} from 'webpack' 6 | */ 7 | 8 | /** 9 | * @typedef {Omit} Options 10 | * Configuration (TypeScript type). 11 | * 12 | * Options are the same as `compile` from `@mdx-js/mdx` with the exception 13 | * that the `SourceMapGenerator` and `development` options are supported 14 | * based on how you configure webpack. 15 | * You cannot pass them manually. 16 | * 17 | * @callback Process 18 | * Process. 19 | * @param {Compatible} vfileCompatible 20 | * Input. 21 | * @returns {Promise} 22 | * File. 23 | */ 24 | 25 | import {Buffer} from 'node:buffer' 26 | import {createHash} from 'node:crypto' 27 | import path from 'node:path' 28 | import {createFormatAwareProcessors} from '@mdx-js/mdx/internal-create-format-aware-processors' 29 | import {SourceMapGenerator} from 'source-map' 30 | 31 | // Note: the cache is heavily inspired by: 32 | // 33 | const marker = /** @type {WebpackCompiler} */ ({}) 34 | /** @type {WeakMap>} */ 35 | const cache = new WeakMap() 36 | 37 | /** 38 | * A Webpack (5+) loader for MDX. 39 | * See `webpack.cjs`, which wraps this, because Webpack loaders must currently 40 | * be CommonJS. 41 | * 42 | * @this {LoaderContext} 43 | * Context. 44 | * @param {string} value 45 | * Value. 46 | * @param {LoaderContext['callback']} callback 47 | * Callback. 48 | * @returns {undefined} 49 | * Nothing. 50 | */ 51 | export function loader(value, callback) { 52 | const options = /** @type {CompileOptions} */ (this.getOptions()) 53 | const hash = getOptionsHash(options) 54 | const config = { 55 | SourceMapGenerator: this.sourceMap ? SourceMapGenerator : undefined, 56 | development: this.mode === 'development', 57 | ...options 58 | } 59 | /* c8 ignore next -- some loaders set `undefined` (see `TypeStrong/ts-loader`). */ 60 | const compiler = this._compiler || marker 61 | 62 | // To do: next major: remove. 63 | if ('renderer' in config) { 64 | callback( 65 | new Error( 66 | '`options.renderer` is no longer supported. Please see for more information' 67 | ) 68 | ) 69 | return 70 | } 71 | 72 | let map = cache.get(compiler) 73 | 74 | if (!map) { 75 | map = new Map() 76 | cache.set(compiler, map) 77 | } 78 | 79 | let process = map.get(hash) 80 | 81 | if (!process) { 82 | process = createFormatAwareProcessors(config).process 83 | map.set(hash, process) 84 | } 85 | 86 | const context = this.context 87 | const filePath = this.resourcePath 88 | 89 | process({value, path: filePath}).then( 90 | function (file) { 91 | callback( 92 | undefined, 93 | // @ts-expect-error: seems to be `@types/node` bug that suddenly started 94 | // to error. 95 | Buffer.from(file.value), 96 | // @ts-expect-error: `webpack` is not compiled with `exactOptionalPropertyTypes`, 97 | // so it does not allow `sourceRoot` in `file.map` to be `undefined` here. 98 | file.map || undefined 99 | ) 100 | }, 101 | /** 102 | * @param {VFileMessage} error 103 | * Error. 104 | * @returns {undefined} 105 | * Nothing. 106 | */ 107 | function (error) { 108 | const fpath = path.relative(context, filePath) 109 | error.message = `${fpath}:${error.name}: ${error.message}` 110 | callback(error) 111 | } 112 | ) 113 | } 114 | 115 | /** 116 | * @param {Readonly} options 117 | * Configuration. 118 | * @returns {string} 119 | * Hash. 120 | */ 121 | function getOptionsHash(options) { 122 | const hash = createHash('sha256') 123 | /** @type {keyof Options} */ 124 | let key 125 | 126 | for (key in options) { 127 | if (Object.hasOwn(options, key)) { 128 | const value = options[key] 129 | 130 | if (value !== undefined) { 131 | const valueString = JSON.stringify(value) 132 | hash.update(key + valueString) 133 | } 134 | } 135 | } 136 | 137 | return hash.digest('hex').slice(0, 16) 138 | } 139 | -------------------------------------------------------------------------------- /packages/loader/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Compositor and Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mdx-js/loader", 3 | "version": "3.1.0", 4 | "description": "Webpack loader for MDX", 5 | "license": "MIT", 6 | "keywords": [ 7 | "jsx", 8 | "markdown", 9 | "mdx", 10 | "preact", 11 | "react", 12 | "remark", 13 | "vue", 14 | "webpack" 15 | ], 16 | "homepage": "https://mdxjs.com", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/mdx-js/mdx", 20 | "directory": "packages/loader/" 21 | }, 22 | "bugs": "https://github.com/mdx-js/mdx/issues", 23 | "funding": { 24 | "type": "opencollective", 25 | "url": "https://opencollective.com/unified" 26 | }, 27 | "author": "John Otander (https://johno.com)", 28 | "contributors": [ 29 | "John Otander (https://johno.com)", 30 | "Tim Neutkens ", 31 | "Matija Marohnić ", 32 | "Titus Wormer (https://wooorm.com)", 33 | "JounQin (https://www.1stg.me)", 34 | "Christian Murphy " 35 | ], 36 | "type": "module", 37 | "sideEffects": false, 38 | "exports": "./index.cjs", 39 | "files": [ 40 | "lib/", 41 | "index.cjs", 42 | "index.d.cts.map", 43 | "index.d.cts" 44 | ], 45 | "dependencies": { 46 | "@mdx-js/mdx": "^3.0.0", 47 | "source-map": "^0.7.0" 48 | }, 49 | "peerDependencies": { 50 | "webpack": ">=5" 51 | }, 52 | "peerDependenciesMeta": { 53 | "webpack": { 54 | "optional": true 55 | } 56 | }, 57 | "devDependencies": {}, 58 | "scripts": { 59 | "test": "npm run test-coverage", 60 | "test-api": "node --conditions development test/index.js", 61 | "test-coverage": "c8 --100 --reporter lcov npm run test-api" 62 | }, 63 | "xo": { 64 | "prettier": true, 65 | "rules": { 66 | "n/file-extension-in-import": "off" 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/loader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/mdx/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('hast-util-to-jsx-runtime').Fragment} Fragment 3 | * @typedef {import('hast-util-to-jsx-runtime').Jsx} Jsx 4 | * @typedef {import('hast-util-to-jsx-runtime').JsxDev} JsxDev 5 | * @typedef {import('./lib/util/resolve-evaluate-options.js').UseMdxComponents} UseMdxComponents 6 | * @typedef {import('./lib/compile.js').CompileOptions} CompileOptions 7 | * @typedef {import('./lib/core.js').ProcessorOptions} ProcessorOptions 8 | * @typedef {import('./lib/util/resolve-evaluate-options.js').EvaluateOptions} EvaluateOptions 9 | * @typedef {import('./lib/util/resolve-evaluate-options.js').RunOptions} RunOptions 10 | */ 11 | 12 | export {compile, compileSync} from './lib/compile.js' 13 | export {createProcessor} from './lib/core.js' 14 | export {evaluate, evaluateSync} from './lib/evaluate.js' 15 | export {nodeTypes} from './lib/node-types.js' 16 | export {run, runSync} from './lib/run.js' 17 | -------------------------------------------------------------------------------- /packages/mdx/lib/compile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Compatible, VFile} from 'vfile' 3 | * @import {ProcessorOptions} from './core.js' 4 | */ 5 | 6 | /** 7 | * @typedef {Omit} CoreProcessorOptions 8 | * Core configuration. 9 | * 10 | * @typedef ExtraOptions 11 | * Extra configuration. 12 | * @property {'detect' | 'md' | 'mdx' | null | undefined} [format='detect'] 13 | * Format of `file` (default: `'detect'`). 14 | * 15 | * @typedef {CoreProcessorOptions & ExtraOptions} CompileOptions 16 | * Configuration for `compile`. 17 | * 18 | * `CompileOptions` is the same as `ProcessorOptions` with the exception that 19 | * the `format` option supports a `'detect'` value, which is the default. 20 | * The `'detect'` format means to use `'md'` for files with an extension in 21 | * `mdExtensions` and `'mdx'` otherwise. 22 | */ 23 | 24 | import {resolveFileAndOptions} from './util/resolve-file-and-options.js' 25 | import {createProcessor} from './core.js' 26 | 27 | /** 28 | * Compile MDX to JS. 29 | * 30 | * @param {Readonly} vfileCompatible 31 | * MDX document to parse. 32 | * @param {Readonly | null | undefined} [compileOptions] 33 | * Compile configuration (optional). 34 | * @return {Promise} 35 | * Promise to compiled file. 36 | */ 37 | export function compile(vfileCompatible, compileOptions) { 38 | const {file, options} = resolveFileAndOptions(vfileCompatible, compileOptions) 39 | return createProcessor(options).process(file) 40 | } 41 | 42 | /** 43 | * Synchronously compile MDX to JS. 44 | * 45 | * When possible please use the async `compile`. 46 | * 47 | * @param {Readonly} vfileCompatible 48 | * MDX document to parse. 49 | * @param {Readonly | null | undefined} [compileOptions] 50 | * Compile configuration (optional). 51 | * @return {VFile} 52 | * Compiled file. 53 | */ 54 | export function compileSync(vfileCompatible, compileOptions) { 55 | const {file, options} = resolveFileAndOptions(vfileCompatible, compileOptions) 56 | return createProcessor(options).processSync(file) 57 | } 58 | -------------------------------------------------------------------------------- /packages/mdx/lib/evaluate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {MDXModule} from 'mdx/types.js' 3 | * @import {Compatible} from 'vfile' 4 | * @import {EvaluateOptions} from './util/resolve-evaluate-options.js' 5 | */ 6 | 7 | import {resolveEvaluateOptions} from './util/resolve-evaluate-options.js' 8 | import {compile, compileSync} from './compile.js' 9 | import {run, runSync} from './run.js' 10 | 11 | /** 12 | * Compile and run MDX. 13 | * 14 | * When you trust your content, `evaluate` can work. 15 | * When possible, use `compile`, write to a file, and then run with Node or use 16 | * one of the integrations. 17 | * 18 | * > ☢️ **Danger**: it’s called **evaluate** because it `eval`s JavaScript. 19 | * 20 | * ###### Notes 21 | * 22 | * Compiling (and running) MDX takes time. 23 | * 24 | * If you are live-rendering a string of MDX that often changes using a virtual 25 | * DOM based framework (such as React), one performance improvement is to call 26 | * the `MDXContent` component yourself. 27 | * The reason is that the `evaluate` creates a new function each time, which 28 | * cannot be diffed: 29 | * 30 | * ```diff 31 | * const {default: MDXContent} = await evaluate('…') 32 | * 33 | * - 34 | * +MDXContent(props) 35 | * ``` 36 | * 37 | * @param {Readonly} file 38 | * MDX document to parse. 39 | * @param {Readonly} options 40 | * Configuration (**required**). 41 | * @return {Promise} 42 | * Promise to a module; 43 | * the result is an object with a `default` field set to the component; 44 | * anything else that was exported is available too. 45 | 46 | */ 47 | export async function evaluate(file, options) { 48 | const {compiletime, runtime} = resolveEvaluateOptions(options) 49 | return run(await compile(file, compiletime), runtime) 50 | } 51 | 52 | /** 53 | * Compile and run MDX, synchronously. 54 | * 55 | * When possible please use the async `evaluate`. 56 | * 57 | * > ☢️ **Danger**: it’s called **evaluate** because it `eval`s JavaScript. 58 | * 59 | * @param {Readonly} file 60 | * MDX document to parse. 61 | * @param {Readonly} options 62 | * Configuration (**required**). 63 | * @return {MDXModule} 64 | * Module. 65 | */ 66 | export function evaluateSync(file, options) { 67 | const {compiletime, runtime} = resolveEvaluateOptions(options) 68 | return runSync(compileSync(file, compiletime), runtime) 69 | } 70 | -------------------------------------------------------------------------------- /packages/mdx/lib/node-types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List of node types made by `mdast-util-mdx`, which have to be passed 3 | * through untouched from the mdast tree to the hast tree. 4 | */ 5 | export const nodeTypes = /** @type {const} */ ([ 6 | 'mdxFlowExpression', 7 | 'mdxJsxFlowElement', 8 | 'mdxJsxTextElement', 9 | 'mdxTextExpression', 10 | 'mdxjsEsm' 11 | ]) 12 | -------------------------------------------------------------------------------- /packages/mdx/lib/plugin/recma-build-jsx-transform.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Program} from 'estree-jsx' 3 | */ 4 | 5 | /** 6 | * @typedef Options 7 | * Configuration for internal plugin `recma-build-jsx-transform`. 8 | * @property {'function-body' | 'program' | null | undefined} [outputFormat='program'] 9 | * Whether to keep the import of the automatic runtime or get it from 10 | * `arguments[0]` instead (default: `'program'`). 11 | */ 12 | 13 | import {specifiersToDeclarations} from '../util/estree-util-specifiers-to-declarations.js' 14 | import {toIdOrMemberExpression} from '../util/estree-util-to-id-or-member-expression.js' 15 | 16 | /** 17 | * Plugin to change the tree after compiling JSX away. 18 | * 19 | * @param {Readonly | null | undefined} [options] 20 | * Configuration (optional). 21 | * @returns 22 | * Transform. 23 | */ 24 | export function recmaBuildJsxTransform(options) { 25 | /* c8 ignore next -- always given in `@mdx-js/mdx` */ 26 | const {outputFormat} = options || {} 27 | 28 | /** 29 | * @param {Program} tree 30 | * Tree. 31 | * @returns {undefined} 32 | * Nothing. 33 | */ 34 | return function (tree) { 35 | // Remove the pragma comment that we injected ourselves as it is no longer 36 | // needed. 37 | if (tree.comments) { 38 | tree.comments = tree.comments.filter(function (d) { 39 | return !d.data?._mdxIsPragmaComment 40 | }) 41 | } 42 | 43 | // When compiling to a function body, replace the import that was just 44 | // generated, and get `jsx`, `jsxs`, and `Fragment` from `arguments[0]` 45 | // instead. 46 | if (outputFormat === 'function-body') { 47 | let index = 0 48 | 49 | // Skip directives: JS currently only has `use strict`, but Acorn allows 50 | // arbitrary ones. 51 | // Practically things like `use client` could be used? 52 | while (index < tree.body.length) { 53 | const child = tree.body[index] 54 | if ('directive' in child && child.directive) { 55 | index++ 56 | } else { 57 | break 58 | } 59 | } 60 | 61 | const declaration = tree.body[index] 62 | 63 | if ( 64 | declaration && 65 | declaration.type === 'ImportDeclaration' && 66 | typeof declaration.source.value === 'string' && 67 | /\/jsx-(dev-)?runtime$/.test(declaration.source.value) 68 | ) { 69 | tree.body[index] = { 70 | type: 'VariableDeclaration', 71 | kind: 'const', 72 | declarations: specifiersToDeclarations( 73 | declaration.specifiers, 74 | toIdOrMemberExpression(['arguments', 0]) 75 | ) 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/mdx/lib/plugin/rehype-remove-raw.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Root} from 'hast' 3 | */ 4 | 5 | import {visit} from 'unist-util-visit' 6 | 7 | /** 8 | * A tiny plugin that removes raw HTML. 9 | * 10 | * This is needed if the format is `md` and `rehype-raw` was not used to parse 11 | * dangerous HTML into nodes. 12 | * 13 | * @returns 14 | * Transform. 15 | */ 16 | export function rehypeRemoveRaw() { 17 | /** 18 | * @param {Root} tree 19 | * Tree. 20 | * @returns {undefined} 21 | * Nothing. 22 | */ 23 | return function (tree) { 24 | visit(tree, 'raw', function (_, index, parent) { 25 | if (parent && typeof index === 'number') { 26 | parent.children.splice(index, 1) 27 | return index 28 | } 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/mdx/lib/plugin/remark-mark-and-unravel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Root, RootContent} from 'mdast' 3 | */ 4 | 5 | import {collapseWhiteSpace} from 'collapse-white-space' 6 | import {walk} from 'estree-walker' 7 | import {visit} from 'unist-util-visit' 8 | 9 | /** 10 | * A tiny plugin that unravels `

    x

    ` but also 11 | * `

    ` (so it has no knowledge of “HTML”). 12 | * 13 | * It also marks JSX as being explicitly JSX, so when a user passes a `h1` 14 | * component, it is used for `# heading` but not for `

    heading

    `. 15 | * 16 | * @returns 17 | * Transform. 18 | */ 19 | export function remarkMarkAndUnravel() { 20 | /** 21 | * @param {Root} tree 22 | * Tree. 23 | * @returns {undefined} 24 | * Nothing. 25 | */ 26 | return function (tree) { 27 | visit(tree, function (node, index, parent) { 28 | let offset = -1 29 | let all = true 30 | let oneOrMore = false 31 | 32 | if (parent && typeof index === 'number' && node.type === 'paragraph') { 33 | const children = node.children 34 | 35 | while (++offset < children.length) { 36 | const child = children[offset] 37 | 38 | if ( 39 | child.type === 'mdxJsxTextElement' || 40 | child.type === 'mdxTextExpression' 41 | ) { 42 | oneOrMore = true 43 | } else if ( 44 | child.type === 'text' && 45 | collapseWhiteSpace(child.value, {style: 'html', trim: true}) === '' 46 | ) { 47 | // Empty. 48 | } else { 49 | all = false 50 | break 51 | } 52 | } 53 | 54 | if (all && oneOrMore) { 55 | offset = -1 56 | 57 | /** @type {Array} */ 58 | const newChildren = [] 59 | 60 | while (++offset < children.length) { 61 | const child = children[offset] 62 | 63 | if (child.type === 'mdxJsxTextElement') { 64 | // @ts-expect-error: mutate because it is faster; content model is fine. 65 | child.type = 'mdxJsxFlowElement' 66 | } 67 | 68 | if (child.type === 'mdxTextExpression') { 69 | // @ts-expect-error: mutate because it is faster; content model is fine. 70 | child.type = 'mdxFlowExpression' 71 | } 72 | 73 | if ( 74 | child.type === 'text' && 75 | /^[\t\r\n ]+$/.test(String(child.value)) 76 | ) { 77 | // Empty. 78 | } else { 79 | newChildren.push(child) 80 | } 81 | } 82 | 83 | parent.children.splice(index, 1, ...newChildren) 84 | return index 85 | } 86 | } 87 | 88 | if ( 89 | node.type === 'mdxJsxFlowElement' || 90 | node.type === 'mdxJsxTextElement' 91 | ) { 92 | const data = node.data || (node.data = {}) 93 | data._mdxExplicitJsx = true 94 | } 95 | 96 | if ( 97 | (node.type === 'mdxFlowExpression' || 98 | node.type === 'mdxTextExpression' || 99 | node.type === 'mdxjsEsm') && 100 | node.data && 101 | node.data.estree 102 | ) { 103 | walk(node.data.estree, { 104 | enter(node) { 105 | if (node.type === 'JSXElement') { 106 | const data = node.data || (node.data = {}) 107 | data._mdxExplicitJsx = true 108 | } 109 | } 110 | }) 111 | } 112 | }) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /packages/mdx/lib/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {MDXModule} from 'mdx/types.js' 3 | * @import {RunOptions} from './util/resolve-evaluate-options.js' 4 | */ 5 | 6 | /** @type {new (code: string, ...args: Array) => Function} **/ 7 | const AsyncFunction = Object.getPrototypeOf(run).constructor 8 | 9 | /** 10 | * Run code compiled with `outputFormat: 'function-body'`. 11 | * 12 | * > ☢️ **Danger**: this `eval`s JavaScript. 13 | * 14 | * @param {{toString(): string}} code 15 | * JavaScript function body to run. 16 | * @param {RunOptions} options 17 | * Configuration (**required**). 18 | * @return {Promise} 19 | * Promise to a module; 20 | * the result is an object with a `default` field set to the component; 21 | * anything else that was exported is available too. 22 | */ 23 | export async function run(code, options) { 24 | return new AsyncFunction(String(code))(options) 25 | } 26 | 27 | /** 28 | * Run code, synchronously. 29 | * 30 | * When possible please use the async `run`. 31 | * 32 | * > ☢️ **Danger**: this `eval`s JavaScript. 33 | * 34 | * @param {{toString(): string}} code 35 | * JavaScript function body to run. 36 | * @param {RunOptions} options 37 | * Configuration (**required**). 38 | * @return {MDXModule} 39 | * Module. 40 | */ 41 | export function runSync(code, options) { 42 | // eslint-disable-next-line no-new-func 43 | return new Function(String(code))(options) 44 | } 45 | -------------------------------------------------------------------------------- /packages/mdx/lib/types.d.ts: -------------------------------------------------------------------------------- 1 | import type {Program as EstreeProgram} from 'estree' 2 | import type {Data as UnistData} from 'unist' 3 | 4 | interface EsastData extends UnistData { 5 | /** 6 | * Whether a node was authored as explicit JSX (`

    `) or as implicitly 7 | * turned into JSX (`# hi`). 8 | * 9 | * Registered by `@mdx-js/mdx/lib/types.d.ts`. 10 | */ 11 | _mdxExplicitJsx?: boolean | null | undefined 12 | } 13 | 14 | interface EsastCommentData extends EsastData { 15 | /** 16 | * Whether a node (only used on comments) was generated by us to include the 17 | * JSX pragmas, so that when we compile JSX away, we can remove it. 18 | * 19 | * Registered by `@mdx-js/mdx/lib/types.d.ts`. 20 | */ 21 | _mdxIsPragmaComment?: boolean | null | undefined 22 | } 23 | 24 | // Register data on `estree`. 25 | declare module 'estree' { 26 | interface BaseNode { 27 | /** 28 | * Extra unist data passed through from mdast through hast to esast. 29 | * 30 | * Registered by `@mdx-js/mdx/lib/types.d.ts`. 31 | */ 32 | data?: EsastData | undefined 33 | } 34 | 35 | interface Comment { 36 | /** 37 | * Extra unist data added by `recma-document`. 38 | * 39 | * Registered by `@mdx-js/mdx/lib/types.d.ts`. 40 | */ 41 | data?: EsastCommentData | undefined 42 | } 43 | } 44 | 45 | // Register data on `mdast`. 46 | declare module 'mdast-util-mdx-jsx' { 47 | interface MdxJsxFlowElementData { 48 | /** 49 | * Whether a node was authored as explicit JSX (`

    `) or as implicitly 50 | * turned into JSX (`# hi`). 51 | * 52 | * Registered by `@mdx-js/mdx/lib/types.d.ts`. 53 | */ 54 | _mdxExplicitJsx?: boolean | null | undefined 55 | } 56 | 57 | interface MdxJsxTextElementData { 58 | /** 59 | * Whether a node was authored as explicit JSX (`

    `) or as implicitly 60 | * turned into JSX (`# hi`). 61 | * 62 | * Registered by `@mdx-js/mdx/lib/types.d.ts`. 63 | */ 64 | _mdxExplicitJsx?: boolean | null | undefined 65 | } 66 | } 67 | 68 | declare module 'unified' { 69 | interface CompileResultMap { 70 | EstreeProgram: EstreeProgram 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/create-format-aware-processors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Program} from 'estree-jsx' 3 | * @import {Root} from 'mdast' 4 | * @import {Processor} from 'unified' 5 | * @import {Compatible, VFile} from 'vfile' 6 | * @import {CompileOptions} from '../compile.js' 7 | */ 8 | 9 | /** 10 | * @typedef FormatAwareProcessors 11 | * Result. 12 | * @property {ReadonlyArray} extnames 13 | * Extensions to use. 14 | * @property {Process} process 15 | * Smart processor, async. 16 | * 17 | * @callback Process 18 | * Smart processor. 19 | * @param {Compatible} vfileCompatible 20 | * MDX or markdown document. 21 | * @return {Promise} 22 | * File. 23 | */ 24 | 25 | import {createProcessor} from '../core.js' 26 | import {md, mdx} from './extnames.js' 27 | import {resolveFileAndOptions} from './resolve-file-and-options.js' 28 | 29 | /** 30 | * Create smart processors to handle different formats. 31 | * 32 | * @param {Readonly | null | undefined} [compileOptions] 33 | * Configuration (optional). 34 | * @return {FormatAwareProcessors} 35 | * Smart processor. 36 | */ 37 | export function createFormatAwareProcessors(compileOptions) { 38 | const compileOptions_ = compileOptions || {} 39 | const mdExtensions = compileOptions_.mdExtensions || md 40 | const mdxExtensions = compileOptions_.mdxExtensions || mdx 41 | /** @type {Processor} */ 42 | let cachedMarkdown 43 | /** @type {Processor} */ 44 | let cachedMdx 45 | 46 | return { 47 | extnames: 48 | compileOptions_.format === 'md' 49 | ? mdExtensions 50 | : compileOptions_.format === 'mdx' 51 | ? mdxExtensions 52 | : [...mdExtensions, ...mdxExtensions], 53 | process 54 | } 55 | 56 | /** 57 | * Smart processor. 58 | * 59 | * @type {Process} 60 | */ 61 | function process(vfileCompatible) { 62 | const {file, processor} = split(vfileCompatible) 63 | return processor.process(file) 64 | } 65 | 66 | /** 67 | * Make a full vfile from what’s given, and figure out which processor 68 | * should be used for it. 69 | * This caches processors (one for markdown and one for MDX) so that they do 70 | * not have to be reconstructed for each file. 71 | * 72 | * @param {Compatible} vfileCompatible 73 | * MDX or markdown document. 74 | * @return {{file: VFile, processor: Processor}} 75 | * File and corresponding processor. 76 | */ 77 | function split(vfileCompatible) { 78 | const {file, options} = resolveFileAndOptions( 79 | vfileCompatible, 80 | compileOptions_ 81 | ) 82 | const processor = 83 | options.format === 'md' 84 | ? cachedMarkdown || (cachedMarkdown = createProcessor(options)) 85 | : cachedMdx || (cachedMdx = createProcessor(options)) 86 | return {file, processor} 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/estree-util-create.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Node} from 'estree-jsx' 3 | */ 4 | 5 | // Fix to show references to above types in VS Code. 6 | '' 7 | 8 | /** 9 | * @param {Readonly} from 10 | * Node to take from. 11 | * @param {Node} to 12 | * Node to add to. 13 | * @returns {undefined} 14 | * Nothing. 15 | */ 16 | export function create(from, to) { 17 | /** @type {Array} */ 18 | const fields = ['start', 'end', 'loc', 'range'] 19 | let index = -1 20 | 21 | while (++index < fields.length) { 22 | const field = fields[index] 23 | 24 | if (field in from) { 25 | // @ts-expect-error: assume they’re settable. 26 | to[field] = from[field] 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/estree-util-declaration-to-expression.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import { 3 | Declaration, 4 | Expression, 5 | MaybeNamedClassDeclaration, 6 | MaybeNamedFunctionDeclaration 7 | * } from 'estree-jsx' 8 | */ 9 | 10 | import {ok as assert} from 'devlop' 11 | 12 | /** 13 | * Turn a declaration into an expression. 14 | * 15 | * Doesn’t work for variable declarations, but that’s fine for our use case 16 | * because currently we’re using this utility for export default declarations, 17 | * which can’t contain variable declarations. 18 | * 19 | * @param {Readonly} declaration 20 | * Declaration. 21 | * @returns {Expression} 22 | * Expression. 23 | */ 24 | export function declarationToExpression(declaration) { 25 | if (declaration.type === 'FunctionDeclaration') { 26 | return {...declaration, type: 'FunctionExpression'} 27 | } 28 | 29 | // This is currently an internal utility so the next shouldn’t happen or a 30 | // maintainer is making a mistake. 31 | assert(declaration.type === 'ClassDeclaration', 'unexpected node type') 32 | return {...declaration, type: 'ClassExpression'} 33 | } 34 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/estree-util-is-declaration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import { 3 | Declaration, 4 | MaybeNamedClassDeclaration, 5 | MaybeNamedFunctionDeclaration, 6 | Node 7 | * } from 'estree-jsx' 8 | */ 9 | 10 | // Fix to show references to above types in VS Code. 11 | '' 12 | 13 | /** 14 | * Check if `node` is a declaration. 15 | * 16 | * @param {Readonly} node 17 | * Node to check. 18 | * @returns {node is Declaration | MaybeNamedClassDeclaration | MaybeNamedFunctionDeclaration} 19 | * Whether `node` is a declaration. 20 | */ 21 | export function isDeclaration(node) { 22 | return Boolean( 23 | node.type === 'FunctionDeclaration' || 24 | node.type === 'ClassDeclaration' || 25 | node.type === 'VariableDeclaration' 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/estree-util-specifiers-to-declarations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import { 3 | AssignmentProperty, 4 | ExportSpecifier, 5 | Expression, 6 | Identifier, 7 | ImportDefaultSpecifier, 8 | ImportNamespaceSpecifier, 9 | ImportSpecifier, 10 | Literal, 11 | VariableDeclarator 12 | * } from 'estree-jsx' 13 | */ 14 | 15 | import {ok as assert} from 'devlop' 16 | import {create} from './estree-util-create.js' 17 | 18 | /** 19 | * @param {ReadonlyArray | Readonly | Readonly | Readonly>} specifiers 20 | * Specifiers. 21 | * @param {Readonly} init 22 | * Initializer. 23 | * @returns {Array} 24 | * Declarations. 25 | */ 26 | export function specifiersToDeclarations(specifiers, init) { 27 | let index = -1 28 | /** @type {Array} */ 29 | const declarations = [] 30 | /** @type {Array} */ 31 | const otherSpecifiers = [] 32 | // Can only be one according to JS syntax. 33 | /** @type {ImportNamespaceSpecifier | undefined} */ 34 | let importNamespaceSpecifier 35 | 36 | while (++index < specifiers.length) { 37 | const specifier = specifiers[index] 38 | 39 | if (specifier.type === 'ImportNamespaceSpecifier') { 40 | importNamespaceSpecifier = specifier 41 | } else { 42 | otherSpecifiers.push(specifier) 43 | } 44 | } 45 | 46 | if (importNamespaceSpecifier) { 47 | /** @type {VariableDeclarator} */ 48 | const declarator = { 49 | type: 'VariableDeclarator', 50 | id: importNamespaceSpecifier.local, 51 | init 52 | } 53 | create(importNamespaceSpecifier, declarator) 54 | declarations.push(declarator) 55 | } 56 | 57 | declarations.push({ 58 | type: 'VariableDeclarator', 59 | id: { 60 | type: 'ObjectPattern', 61 | properties: otherSpecifiers.map(function (specifier) { 62 | /** @type {Identifier | Literal} */ 63 | let key = 64 | specifier.type === 'ImportSpecifier' 65 | ? specifier.imported 66 | : specifier.type === 'ExportSpecifier' 67 | ? specifier.exported 68 | : {type: 'Identifier', name: 'default'} 69 | let value = specifier.local 70 | 71 | // Switch them around if we’re exporting. 72 | if (specifier.type === 'ExportSpecifier') { 73 | value = key 74 | key = specifier.local 75 | } 76 | 77 | // To do: what to do about literals? 78 | // `const { a: 'b' } = c()` does not work? 79 | assert(value.type === 'Identifier') 80 | 81 | /** @type {AssignmentProperty} */ 82 | const property = { 83 | type: 'Property', 84 | kind: 'init', 85 | shorthand: 86 | key.type === 'Identifier' && 87 | value.type === 'Identifier' && 88 | key.name === value.name, 89 | method: false, 90 | computed: false, 91 | key, 92 | value 93 | } 94 | create(specifier, property) 95 | return property 96 | }) 97 | }, 98 | init: importNamespaceSpecifier 99 | ? {type: 'Identifier', name: importNamespaceSpecifier.local.name} 100 | : init 101 | }) 102 | 103 | return declarations 104 | } 105 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/estree-util-to-binary-addition.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Expression} from 'estree-jsx' 3 | */ 4 | 5 | import {ok as assert} from 'devlop' 6 | 7 | /** 8 | * @param {ReadonlyArray} expressions 9 | * Expressions. 10 | * @returns {Expression} 11 | * Addition. 12 | */ 13 | export function toBinaryAddition(expressions) { 14 | let index = -1 15 | /** @type {Expression | undefined} */ 16 | let left 17 | 18 | while (++index < expressions.length) { 19 | const right = expressions[index] 20 | left = left ? {type: 'BinaryExpression', left, operator: '+', right} : right 21 | } 22 | 23 | assert(left, 'expected non-empty `expressions` to be passed') 24 | return left 25 | } 26 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/estree-util-to-id-or-member-expression.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import { 3 | Identifier, 4 | JSXIdentifier, 5 | JSXMemberExpression, 6 | Literal, 7 | MemberExpression 8 | * } from 'estree-jsx' 9 | */ 10 | 11 | import {ok as assert} from 'devlop' 12 | import {name as isIdentifierName} from 'estree-util-is-identifier-name' 13 | 14 | /** 15 | * @param {ReadonlyArray} ids 16 | * Identifiers (example: `['list', 0]). 17 | * @returns {Identifier | MemberExpression} 18 | * Identifier or member expression. 19 | */ 20 | export function toIdOrMemberExpression(ids) { 21 | let index = -1 22 | /** @type {Identifier | Literal | MemberExpression | undefined} */ 23 | let object 24 | 25 | while (++index < ids.length) { 26 | const name = ids[index] 27 | /** @type {Identifier | Literal} */ 28 | const id = 29 | typeof name === 'string' && isIdentifierName(name) 30 | ? {type: 'Identifier', name} 31 | : {type: 'Literal', value: name} 32 | object = object 33 | ? { 34 | type: 'MemberExpression', 35 | object, 36 | property: id, 37 | computed: id.type === 'Literal', 38 | optional: false 39 | } 40 | : id 41 | } 42 | 43 | assert(object, 'expected non-empty `ids` to be passed') 44 | assert(object.type !== 'Literal', 'expected identifier as left-most value') 45 | return object 46 | } 47 | 48 | /** 49 | * @param {ReadonlyArray} ids 50 | * Identifiers (example: `['list', 0]). 51 | * @returns {JSXIdentifier | JSXMemberExpression} 52 | * Identifier or member expression. 53 | */ 54 | export function toJsxIdOrMemberExpression(ids) { 55 | let index = -1 56 | /** @type {JSXIdentifier | JSXMemberExpression | undefined} */ 57 | let object 58 | 59 | while (++index < ids.length) { 60 | const name = ids[index] 61 | assert( 62 | typeof name === 'string' && isIdentifierName(name, {jsx: true}), 63 | 'expected valid jsx identifier, not `' + name + '`' 64 | ) 65 | 66 | /** @type {JSXIdentifier} */ 67 | const id = {type: 'JSXIdentifier', name} 68 | object = object ? {type: 'JSXMemberExpression', object, property: id} : id 69 | } 70 | 71 | assert(object, 'expected non-empty `ids` to be passed') 72 | return object 73 | } 74 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/extnames-to-regex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Turn a list of extnames (*with* dots) into an expression. 3 | * 4 | * @param {ReadonlyArray} extnames 5 | * List of extnames. 6 | * @returns {RegExp} 7 | * Regex matching them. 8 | */ 9 | export function extnamesToRegex(extnames) { 10 | return new RegExp( 11 | '\\.(' + 12 | extnames 13 | .map(function (d) { 14 | return d.slice(1) 15 | }) 16 | .join('|') + 17 | ')([?#]|$)' 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/extnames.js: -------------------------------------------------------------------------------- 1 | import markdownExtensions from 'markdown-extensions' 2 | 3 | export const md = markdownExtensions.map(function (d) { 4 | return '.' + d 5 | }) 6 | export const mdx = ['.mdx'] 7 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/resolve-evaluate-options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Fragment, Jsx, JsxDev} from 'hast-util-to-jsx-runtime' 3 | * @import {MDXComponents} from 'mdx/types.js' 4 | * @import {CompileOptions} from '../compile.js' 5 | */ 6 | 7 | /** 8 | * @typedef {EvaluateProcessorOptions & RunOptions} EvaluateOptions 9 | * Configuration for `evaluate`. 10 | * 11 | * @typedef {Omit } EvaluateProcessorOptions 12 | * Compile configuration without JSX options for evaluation. 13 | * 14 | * @typedef RunOptions 15 | * Configuration to run compiled code. 16 | * 17 | * `Fragment`, `jsx`, and `jsxs` are used when the code is compiled in 18 | * production mode (`development: false`). 19 | * `Fragment` and `jsxDEV` are used when compiled in development mode 20 | * (`development: true`). 21 | * `useMDXComponents` is used when the code is compiled with 22 | * `providerImportSource: '#'` (the exact value of this compile option 23 | * doesn’t matter). 24 | * @property {URL | string | null | undefined} [baseUrl] 25 | * Use this URL as `import.meta.url` and resolve `import` and `export … from` 26 | * relative to it (optional, example: `import.meta.url`); 27 | * this option can also be given at compile time in `CompileOptions`; 28 | * you should pass this (likely at runtime), as you might get runtime errors 29 | * when using `import.meta.url` / `import` / `export … from ` otherwise. 30 | * @property {Fragment} Fragment 31 | * Symbol to use for fragments (**required**). 32 | * @property {Jsx | null | undefined} [jsx] 33 | * Function to generate an element with static children in production mode. 34 | * @property {JsxDev | null | undefined} [jsxDEV] 35 | * Function to generate an element in development mode. 36 | * @property {Jsx | null | undefined} [jsxs] 37 | * Function to generate an element with dynamic children in production mode. 38 | * @property {UseMdxComponents | null | undefined} [useMDXComponents] 39 | * Function to get components from context. 40 | * 41 | * @callback UseMdxComponents 42 | * Get components from context. 43 | * @returns {MDXComponents} 44 | * Current components. 45 | */ 46 | 47 | // Fix to show references to above types in VS Code. 48 | '' 49 | 50 | /** 51 | * Split compiletime options from runtime options. 52 | * 53 | * @param {Readonly | null | undefined} options 54 | * Configuration. 55 | * @returns {{compiletime: CompileOptions, runtime: RunOptions}} 56 | * Split options. 57 | */ 58 | export function resolveEvaluateOptions(options) { 59 | const { 60 | Fragment, 61 | baseUrl, 62 | development, 63 | jsx, 64 | jsxDEV, 65 | jsxs, 66 | useMDXComponents, 67 | ...rest 68 | } = options || {} 69 | 70 | if (!Fragment) throw new Error('Expected `Fragment` given to `evaluate`') 71 | if (development) { 72 | if (!jsxDEV) throw new Error('Expected `jsxDEV` given to `evaluate`') 73 | } else { 74 | if (!jsx) throw new Error('Expected `jsx` given to `evaluate`') 75 | if (!jsxs) throw new Error('Expected `jsxs` given to `evaluate`') 76 | } 77 | 78 | return { 79 | compiletime: { 80 | ...rest, 81 | development, 82 | outputFormat: 'function-body', 83 | providerImportSource: useMDXComponents ? '#' : undefined 84 | }, 85 | runtime: {Fragment, baseUrl, jsx, jsxDEV, jsxs, useMDXComponents} 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packages/mdx/lib/util/resolve-file-and-options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {Compatible} from 'vfile' 3 | * @import {CompileOptions} from '../compile.js' 4 | * @import {ProcessorOptions} from '../core.js' 5 | */ 6 | 7 | import {VFile} from 'vfile' 8 | import {md} from './extnames.js' 9 | 10 | /** 11 | * Create a file and options from a given `vfileCompatible` and options that 12 | * might contain `format: 'detect'`. 13 | * 14 | * @param {Readonly} vfileCompatible 15 | * File. 16 | * @param {Readonly | null | undefined} [options] 17 | * Configuration (optional). 18 | * @returns {{file: VFile, options: ProcessorOptions}} 19 | * File and options. 20 | */ 21 | export function resolveFileAndOptions(vfileCompatible, options) { 22 | const file = looksLikeAVFile(vfileCompatible) 23 | ? vfileCompatible 24 | : new VFile(vfileCompatible) 25 | const {format, ...rest} = options || {} 26 | return { 27 | file, 28 | options: { 29 | format: 30 | format === 'md' || format === 'mdx' 31 | ? format 32 | : file.extname && (rest.mdExtensions || md).includes(file.extname) 33 | ? 'md' 34 | : 'mdx', 35 | ...rest 36 | } 37 | } 38 | } 39 | 40 | /** 41 | * @param {Readonly | null | undefined} [value] 42 | * Thing. 43 | * @returns {value is VFile} 44 | * Check. 45 | */ 46 | function looksLikeAVFile(value) { 47 | return Boolean( 48 | value && 49 | typeof value === 'object' && 50 | 'message' in value && 51 | 'messages' in value 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /packages/mdx/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Compositor, Inc. and Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/mdx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mdx-js/mdx", 3 | "version": "3.1.0", 4 | "description": "MDX compiler", 5 | "license": "MIT", 6 | "keywords": [ 7 | "jsx", 8 | "markdown", 9 | "mdx", 10 | "remark" 11 | ], 12 | "homepage": "https://mdxjs.com", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/mdx-js/mdx", 16 | "directory": "packages/mdx/" 17 | }, 18 | "bugs": "https://github.com/mdx-js/mdx/issues", 19 | "funding": { 20 | "type": "opencollective", 21 | "url": "https://opencollective.com/unified" 22 | }, 23 | "author": "John Otander (https://johno.com)", 24 | "contributors": [ 25 | "John Otander (https://johno.com)", 26 | "Tim Neutkens ", 27 | "Matija Marohnić ", 28 | "Titus Wormer (https://wooorm.com)", 29 | "JounQin (https://www.1stg.me)", 30 | "Christian Murphy " 31 | ], 32 | "type": "module", 33 | "sideEffects": false, 34 | "exports": { 35 | ".": "./index.js", 36 | "./internal-create-format-aware-processors": "./lib/util/create-format-aware-processors.js", 37 | "./internal-extnames-to-regex": "./lib/util/extnames-to-regex.js" 38 | }, 39 | "files": [ 40 | "lib/", 41 | "index.d.ts.map", 42 | "index.d.ts", 43 | "index.js" 44 | ], 45 | "dependencies": { 46 | "@types/estree": "^1.0.0", 47 | "@types/estree-jsx": "^1.0.0", 48 | "@types/hast": "^3.0.0", 49 | "@types/mdx": "^2.0.0", 50 | "collapse-white-space": "^2.0.0", 51 | "devlop": "^1.0.0", 52 | "estree-util-is-identifier-name": "^3.0.0", 53 | "estree-util-scope": "^1.0.0", 54 | "estree-walker": "^3.0.0", 55 | "hast-util-to-jsx-runtime": "^2.0.0", 56 | "markdown-extensions": "^2.0.0", 57 | "recma-build-jsx": "^1.0.0", 58 | "recma-jsx": "^1.0.0", 59 | "recma-stringify": "^1.0.0", 60 | "rehype-recma": "^1.0.0", 61 | "remark-mdx": "^3.0.0", 62 | "remark-parse": "^11.0.0", 63 | "remark-rehype": "^11.0.0", 64 | "source-map": "^0.7.0", 65 | "unified": "^11.0.0", 66 | "unist-util-position-from-estree": "^2.0.0", 67 | "unist-util-stringify-position": "^4.0.0", 68 | "unist-util-visit": "^5.0.0", 69 | "vfile": "^6.0.0" 70 | }, 71 | "scripts": { 72 | "test": "npm run test-coverage", 73 | "test-api": "node --conditions development --enable-source-maps test/index.js", 74 | "test-coverage": "c8 --100 --reporter lcov npm run test-api" 75 | }, 76 | "xo": { 77 | "overrides": [ 78 | { 79 | "files": [ 80 | "test/**/*.js" 81 | ], 82 | "rules": { 83 | "no-restricted-globals": "off" 84 | } 85 | }, 86 | { 87 | "files": [ 88 | "**/*.ts" 89 | ], 90 | "rules": { 91 | "@typescript-eslint/array-type": "off", 92 | "@typescript-eslint/ban-types": "off", 93 | "@typescript-eslint/consistent-type-definitions": "off" 94 | } 95 | } 96 | ], 97 | "prettier": true, 98 | "rules": { 99 | "complexity": "off", 100 | "logical-assignment-operators": "off", 101 | "max-depth": "off", 102 | "n/file-extension-in-import": "off", 103 | "unicorn/prefer-at": "off", 104 | "unicorn/prefer-code-point": "off" 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /packages/mdx/test/context/components.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {ComponentProps} from 'react' 3 | */ 4 | 5 | import React from 'react' 6 | 7 | /** 8 | * @param {Readonly>} properties 9 | * Properties. 10 | * @returns 11 | * `span` element. 12 | */ 13 | export function Pill(properties) { 14 | return React.createElement('span', {...properties, style: {color: 'red'}}) 15 | } 16 | 17 | /** 18 | * @param {Readonly>} properties 19 | * Properties. 20 | * @returns 21 | * `div` element. 22 | */ 23 | export function Layout(properties) { 24 | return React.createElement('div', {...properties, style: {color: 'red'}}) 25 | } 26 | 27 | export default Layout 28 | -------------------------------------------------------------------------------- /packages/mdx/test/context/data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Number. 3 | */ 4 | export const number = 3.14 5 | 6 | /** 7 | * Object. 8 | */ 9 | export const object = {a: 1, b: 2} 10 | 11 | /** 12 | * Array. 13 | */ 14 | export const array = [1, 2] 15 | 16 | /** 17 | * Number. 18 | */ 19 | export default 2 * number 20 | -------------------------------------------------------------------------------- /packages/mdx/test/context/run.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {MDXContent, MDXModule} from 'mdx/types.js' 3 | * @import {Compatible} from 'vfile' 4 | */ 5 | 6 | import fs from 'node:fs/promises' 7 | 8 | /** 9 | * @param {Readonly} input 10 | * MDX document. 11 | * @return {Promise} 12 | * MDX content. 13 | */ 14 | export async function run(input) { 15 | const result = await runWhole(input) 16 | return result.default 17 | } 18 | 19 | /** 20 | * 21 | * @param {Readonly} input 22 | * MDX document. 23 | * @return {Promise} 24 | * MDX module. 25 | */ 26 | export async function runWhole(input) { 27 | const fileName = 'fixture-' + Math.random() + '.js' 28 | const fileUrl = new URL(fileName, import.meta.url) 29 | const document = String(input) 30 | 31 | await fs.writeFile(fileUrl, document) 32 | 33 | try { 34 | /** @type {MDXModule} */ 35 | return await import(fileUrl.href) 36 | } finally { 37 | // This is not a bug: the `finally` runs after the whole `try` block, but 38 | // before the `return`. 39 | await fs.rm(fileUrl) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/mdx/test/core.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert/strict' 2 | import {test} from 'node:test' 3 | 4 | test('@mdx-js/mdx: core', async function (t) { 5 | await t.test('should expose the public api', async function () { 6 | assert.deepEqual(Object.keys(await import('@mdx-js/mdx')).sort(), [ 7 | 'compile', 8 | 'compileSync', 9 | 'createProcessor', 10 | 'evaluate', 11 | 'evaluateSync', 12 | 'nodeTypes', 13 | 'run', 14 | 'runSync' 15 | ]) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /packages/mdx/test/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unassigned-import */ 2 | import './compile.js' 3 | import './core.js' 4 | import './evaluate.js' 5 | import './syntax.js' 6 | -------------------------------------------------------------------------------- /packages/mdx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/node-loader/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('./lib/index.js').Options} Options 3 | */ 4 | 5 | import {createLoader} from './lib/index.js' 6 | 7 | const defaultLoader = createLoader() 8 | 9 | export {createLoader} from './lib/index.js' 10 | 11 | /** 12 | * Pass options to the loader. 13 | */ 14 | export const initialize = defaultLoader.initialize 15 | 16 | /** 17 | * Load `file:` URLs to MD(X) files. 18 | */ 19 | export const load = defaultLoader.load 20 | -------------------------------------------------------------------------------- /packages/node-loader/lib/condition.default.js: -------------------------------------------------------------------------------- 1 | export const development = false 2 | -------------------------------------------------------------------------------- /packages/node-loader/lib/condition.development.js: -------------------------------------------------------------------------------- 1 | export const development = true 2 | -------------------------------------------------------------------------------- /packages/node-loader/lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {LoadFnOutput, LoadHook, LoadHookContext} from 'node:module' 3 | * @import {Process} from '@mdx-js/mdx/internal-create-format-aware-processors' 4 | * @import {CompileOptions} from '@mdx-js/mdx' 5 | */ 6 | 7 | /** 8 | * @typedef {Parameters[2]} NextLoad 9 | * Next. 10 | * 11 | * @typedef {Omit} Options 12 | * Configuration. 13 | * 14 | * Options are the same as `compile` from `@mdx-js/mdx` with the 15 | * exception that the `development` option is supported based on 16 | * whether you run Node with `--conditions development`. 17 | * You cannot pass it manually. 18 | * 19 | * @typedef {[regex: RegExp, process: Process]} Settings 20 | */ 21 | 22 | import fs from 'node:fs/promises' 23 | import {createFormatAwareProcessors} from '@mdx-js/mdx/internal-create-format-aware-processors' 24 | import {extnamesToRegex} from '@mdx-js/mdx/internal-extnames-to-regex' 25 | import {SourceMapGenerator} from 'source-map' 26 | import {reporter} from 'vfile-reporter' 27 | import {VFile} from 'vfile' 28 | import {development as defaultDevelopment} from '#condition' 29 | 30 | /** 31 | * Create Node.js hooks to handle markdown and MDX. 32 | * 33 | * @param {Readonly | null | undefined} [loaderOptions] 34 | * Configuration (optional). 35 | * @returns 36 | * Node.js hooks. 37 | */ 38 | export function createLoader(loaderOptions) { 39 | /** @type {Settings} */ 40 | let settings = configure(loaderOptions || {}) 41 | 42 | return {initialize, load} 43 | 44 | /** 45 | * 46 | * @param {Readonly | null | undefined} options 47 | */ 48 | async function initialize(options) { 49 | settings = configure({...loaderOptions, ...options}) 50 | } 51 | 52 | /** 53 | * Load `file:` URLs to MD(X) files. 54 | * 55 | * @param {string} href 56 | * URL. 57 | * @param {LoadHookContext} context 58 | * Context. 59 | * @param {NextLoad} nextLoad 60 | * Next or default `load` function. 61 | * @returns {Promise} 62 | * Result. 63 | * @satisfies {LoadHook} 64 | */ 65 | async function load(href, context, nextLoad) { 66 | const url = new URL(href) 67 | const [regex, process] = settings 68 | 69 | if (url.protocol === 'file:' && regex.test(url.pathname)) { 70 | const value = await fs.readFile(url) 71 | const file = await process(new VFile({value, path: url})) 72 | 73 | /* c8 ignore next 3 -- hard to test. */ 74 | if (file.messages.length > 0) { 75 | console.error(reporter(file)) 76 | } 77 | 78 | return { 79 | format: 'module', 80 | shortCircuit: true, 81 | source: 82 | String(file) + 83 | '\n//# sourceMappingURL=data:application/json;base64,' + 84 | Buffer.from(JSON.stringify(file.map)).toString('base64') + 85 | '\n' 86 | } 87 | } 88 | 89 | return nextLoad(href, context) 90 | } 91 | } 92 | 93 | /** 94 | * @param {Readonly} options 95 | * @returns {Settings} 96 | */ 97 | function configure(options) { 98 | const {extnames, process} = createFormatAwareProcessors({ 99 | development: defaultDevelopment, 100 | ...options, 101 | SourceMapGenerator 102 | }) 103 | const regex = extnamesToRegex(extnames) 104 | 105 | return [regex, process] 106 | } 107 | -------------------------------------------------------------------------------- /packages/node-loader/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Titus Wormer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/node-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mdx-js/node-loader", 3 | "version": "3.1.0", 4 | "description": "Node.js loader for MDX", 5 | "license": "MIT", 6 | "keywords": [ 7 | "jsx", 8 | "loader", 9 | "markdown", 10 | "mdx", 11 | "node", 12 | "react", 13 | "remark" 14 | ], 15 | "homepage": "https://mdxjs.com", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/mdx-js/mdx", 19 | "directory": "packages/node-loader/" 20 | }, 21 | "bugs": "https://github.com/mdx-js/mdx/issues", 22 | "funding": { 23 | "type": "opencollective", 24 | "url": "https://opencollective.com/unified" 25 | }, 26 | "author": "John Otander (https://johno.com)", 27 | "contributors": [ 28 | "Titus Wormer (https://wooorm.com)" 29 | ], 30 | "type": "module", 31 | "sideEffects": false, 32 | "exports": "./index.js", 33 | "imports": { 34 | "#condition": { 35 | "development": "./lib/condition.development.js", 36 | "default": "./lib/condition.default.js" 37 | } 38 | }, 39 | "files": [ 40 | "lib/", 41 | "index.d.ts.map", 42 | "index.d.ts", 43 | "index.js" 44 | ], 45 | "dependencies": { 46 | "@mdx-js/mdx": "^3.0.0", 47 | "source-map": "^0.7.0", 48 | "vfile-reporter": "^8.0.0", 49 | "vfile": "^6.0.0" 50 | }, 51 | "devDependencies": {}, 52 | "scripts": { 53 | "test": "npm run test-coverage", 54 | "test-api": "node --conditions development --enable-source-maps --loader=@mdx-js/node-loader test/index.js", 55 | "test-coverage": "c8 --100 --reporter lcov npm run test-api" 56 | }, 57 | "xo": { 58 | "prettier": true, 59 | "rules": { 60 | "n/file-extension-in-import": "off" 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/node-loader/test/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {MDXModule} from 'mdx/types.js' 3 | */ 4 | 5 | import assert from 'node:assert/strict' 6 | import fs from 'node:fs/promises' 7 | import {test} from 'node:test' 8 | import React from 'react' 9 | import {renderToStaticMarkup} from 'react-dom/server' 10 | 11 | test('@mdx-js/node-loader', async function (t) { 12 | await t.test('should expose the public api', async function () { 13 | assert.deepEqual(Object.keys(await import('@mdx-js/node-loader')).sort(), [ 14 | 'createLoader', 15 | 'initialize', 16 | 'load' 17 | ]) 18 | }) 19 | 20 | await t.test('should work', async function () { 21 | const mdxUrl = new URL('node-loader.mdx', import.meta.url) 22 | 23 | await fs.writeFile( 24 | mdxUrl, 25 | 'export function Message() { return <>World! }\n\n# Hello, ' 26 | ) 27 | 28 | /** @type {MDXModule} */ 29 | let result 30 | 31 | try { 32 | result = await import(mdxUrl.href) 33 | } catch (error) { 34 | const exception = /** @type {NodeJS.ErrnoException} */ (error) 35 | 36 | if (exception.code === 'ERR_UNKNOWN_FILE_EXTENSION') { 37 | await fs.rm(mdxUrl) 38 | 39 | throw new Error( 40 | 'Please run Node with `--loader=@mdx-js/node-loader` to test the ESM loader' 41 | ) 42 | } 43 | 44 | throw error 45 | } 46 | 47 | const Content = result.default 48 | 49 | assert.equal( 50 | renderToStaticMarkup(React.createElement(Content)), 51 | '

    Hello, World!

    ' 52 | ) 53 | 54 | await fs.rm(mdxUrl) 55 | }) 56 | 57 | await t.test('supports source maps work', async function () { 58 | const mdxUrl = new URL('crash.mdx', import.meta.url) 59 | 60 | await fs.writeFile( 61 | mdxUrl, 62 | '\nexport function Throw() { throw new Error("Boom") }\n' 63 | ) 64 | 65 | /** @type {MDXModule} */ 66 | let result 67 | 68 | try { 69 | result = await import(mdxUrl.href) 70 | } catch (error) { 71 | const exception = /** @type {NodeJS.ErrnoException} */ (error) 72 | 73 | if (exception.code === 'ERR_UNKNOWN_FILE_EXTENSION') { 74 | await fs.rm(mdxUrl) 75 | 76 | throw new Error( 77 | 'Please run Node with `--loader=@mdx-js/node-loader` to test the ESM loader' 78 | ) 79 | } 80 | 81 | throw error 82 | } 83 | 84 | const Content = result.default 85 | 86 | assert.throws( 87 | () => renderToStaticMarkup(React.createElement(Content)), 88 | (error) => { 89 | assert(error instanceof Error) 90 | assert.equal(error.message, 'Boom') 91 | // Source maps are off. 92 | // The column should be 26, not 8. 93 | assert(error.stack?.includes('crash.mdx:2:8)')) 94 | return true 95 | } 96 | ) 97 | 98 | await fs.rm(mdxUrl) 99 | }) 100 | }) 101 | -------------------------------------------------------------------------------- /packages/node-loader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/preact/index.js: -------------------------------------------------------------------------------- 1 | export {MDXProvider, useMDXComponents} from './lib/index.js' 2 | -------------------------------------------------------------------------------- /packages/preact/lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {MDXComponents} from 'mdx/types.js' 3 | * @import {Component, ComponentChildren, VNode} from 'preact' 4 | */ 5 | 6 | /** 7 | * @callback MergeComponents 8 | * Custom merge function. 9 | * @param {Readonly} currentComponents 10 | * Current components from the context. 11 | * @returns {MDXComponents} 12 | * Additional components. 13 | * 14 | * @typedef Props 15 | * Configuration for `MDXProvider`. 16 | * @property {ComponentChildren} [children] 17 | * Children (optional). 18 | * @property {Readonly | MergeComponents | null | undefined} [components] 19 | * Additional components to use or a function that creates them (optional). 20 | * @property {boolean | null | undefined} [disableParentContext=false] 21 | * Turn off outer component context (default: `false`). 22 | */ 23 | 24 | import {createContext, h} from 'preact' 25 | import {useContext} from 'preact/hooks' 26 | 27 | /** @type {Readonly} */ 28 | const emptyComponents = {} 29 | 30 | const MDXContext = createContext(emptyComponents) 31 | 32 | /** 33 | * Get current components from the MDX Context. 34 | * 35 | * @param {Readonly | MergeComponents | null | undefined} [components] 36 | * Additional components to use or a function that creates them (optional). 37 | * @returns {MDXComponents} 38 | * Current components. 39 | */ 40 | export function useMDXComponents(components) { 41 | const contextComponents = useContext(MDXContext) 42 | 43 | // Custom merge via a function prop 44 | if (typeof components === 'function') { 45 | return components(contextComponents) 46 | } 47 | 48 | return {...contextComponents, ...components} 49 | } 50 | 51 | /** 52 | * Provider for MDX context. 53 | * 54 | * @param {Readonly} properties 55 | * Properties. 56 | * @returns {VNode} 57 | * Element. 58 | * @satisfies {Component} 59 | */ 60 | export function MDXProvider(properties) { 61 | /** @type {Readonly} */ 62 | let allComponents 63 | 64 | if (properties.disableParentContext) { 65 | allComponents = 66 | typeof properties.components === 'function' 67 | ? properties.components(emptyComponents) 68 | : properties.components || emptyComponents 69 | } else { 70 | allComponents = useMDXComponents(properties.components) 71 | } 72 | 73 | return h( 74 | MDXContext.Provider, 75 | {children: undefined, value: allComponents}, 76 | properties.children 77 | ) 78 | } 79 | -------------------------------------------------------------------------------- /packages/preact/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Compositor and Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/preact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mdx-js/preact", 3 | "version": "3.1.0", 4 | "description": "Preact context for MDX", 5 | "license": "MIT", 6 | "keywords": [ 7 | "jsx", 8 | "markdown", 9 | "mdx", 10 | "preact", 11 | "remark" 12 | ], 13 | "homepage": "https://mdxjs.com", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/mdx-js/mdx", 17 | "directory": "packages/preact/" 18 | }, 19 | "bugs": "https://github.com/mdx-js/mdx/issues", 20 | "funding": { 21 | "type": "opencollective", 22 | "url": "https://opencollective.com/unified" 23 | }, 24 | "author": "John Otander (https://johno.com)", 25 | "contributors": [ 26 | "John Otander (https://johno.com)", 27 | "Tim Neutkens ", 28 | "Matija Marohnić ", 29 | "Titus Wormer (https://wooorm.com)", 30 | "JounQin (https://www.1stg.me)", 31 | "Chris Biscardi (https://www.christopherbiscardi.com)", 32 | "Christian Murphy " 33 | ], 34 | "type": "module", 35 | "sideEffects": false, 36 | "exports": "./index.js", 37 | "files": [ 38 | "lib/", 39 | "index.d.ts.map", 40 | "index.d.ts", 41 | "index.js" 42 | ], 43 | "dependencies": { 44 | "@types/mdx": "^2.0.0" 45 | }, 46 | "peerDependencies": { 47 | "preact": ">=10.0.0" 48 | }, 49 | "devDependencies": {}, 50 | "scripts": { 51 | "test": "npm run test-coverage", 52 | "test-api": "node --conditions development --loader=../../script/jsx-loader.js test/index.jsx", 53 | "test-coverage": "c8 --100 --reporter lcov npm run test-api" 54 | }, 55 | "xo": { 56 | "overrides": [ 57 | { 58 | "extends": "xo-react", 59 | "files": [ 60 | "**/*.jsx" 61 | ], 62 | "rules": { 63 | "react/jsx-no-bind": "off", 64 | "react/react-in-jsx-scope": "off" 65 | } 66 | } 67 | ], 68 | "prettier": true, 69 | "rules": { 70 | "n/file-extension-in-import": "off" 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/preact/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/react/index.js: -------------------------------------------------------------------------------- 1 | export {MDXProvider, useMDXComponents} from './lib/index.js' 2 | -------------------------------------------------------------------------------- /packages/react/lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {MDXComponents} from 'mdx/types.js' 3 | * @import {Component, ReactElement, ReactNode} from 'react' 4 | */ 5 | 6 | /** 7 | * @callback MergeComponents 8 | * Custom merge function. 9 | * @param {Readonly} currentComponents 10 | * Current components from the context. 11 | * @returns {MDXComponents} 12 | * Additional components. 13 | * 14 | * @typedef Props 15 | * Configuration for `MDXProvider`. 16 | * @property {ReactNode | null | undefined} [children] 17 | * Children (optional). 18 | * @property {Readonly | MergeComponents | null | undefined} [components] 19 | * Additional components to use or a function that creates them (optional). 20 | * @property {boolean | null | undefined} [disableParentContext=false] 21 | * Turn off outer component context (default: `false`). 22 | */ 23 | 24 | import React from 'react' 25 | 26 | /** @type {Readonly} */ 27 | const emptyComponents = {} 28 | 29 | const MDXContext = React.createContext(emptyComponents) 30 | 31 | /** 32 | * Get current components from the MDX Context. 33 | * 34 | * @param {Readonly | MergeComponents | null | undefined} [components] 35 | * Additional components to use or a function that creates them (optional). 36 | * @returns {MDXComponents} 37 | * Current components. 38 | */ 39 | export function useMDXComponents(components) { 40 | const contextComponents = React.useContext(MDXContext) 41 | 42 | // Memoize to avoid unnecessary top-level context changes 43 | return React.useMemo( 44 | function () { 45 | // Custom merge via a function prop 46 | if (typeof components === 'function') { 47 | return components(contextComponents) 48 | } 49 | 50 | return {...contextComponents, ...components} 51 | }, 52 | [contextComponents, components] 53 | ) 54 | } 55 | 56 | /** 57 | * Provider for MDX context. 58 | * 59 | * @param {Readonly} properties 60 | * Properties. 61 | * @returns {ReactElement} 62 | * Element. 63 | * @satisfies {Component} 64 | */ 65 | export function MDXProvider(properties) { 66 | /** @type {Readonly} */ 67 | let allComponents 68 | 69 | if (properties.disableParentContext) { 70 | allComponents = 71 | typeof properties.components === 'function' 72 | ? properties.components(emptyComponents) 73 | : properties.components || emptyComponents 74 | } else { 75 | allComponents = useMDXComponents(properties.components) 76 | } 77 | 78 | return React.createElement( 79 | MDXContext.Provider, 80 | {value: allComponents}, 81 | properties.children 82 | ) 83 | } 84 | -------------------------------------------------------------------------------- /packages/react/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Compositor and Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mdx-js/react", 3 | "version": "3.1.0", 4 | "description": "React context for MDX", 5 | "license": "MIT", 6 | "keywords": [ 7 | "jsx", 8 | "markdown", 9 | "mdx", 10 | "react", 11 | "remark" 12 | ], 13 | "homepage": "https://mdxjs.com", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/mdx-js/mdx", 17 | "directory": "packages/react/" 18 | }, 19 | "bugs": "https://github.com/mdx-js/mdx/issues", 20 | "funding": { 21 | "type": "opencollective", 22 | "url": "https://opencollective.com/unified" 23 | }, 24 | "author": "John Otander (https://johno.com)", 25 | "contributors": [ 26 | "John Otander (https://johno.com)", 27 | "Tim Neutkens ", 28 | "Matija Marohnić ", 29 | "Titus Wormer (https://wooorm.com)", 30 | "JounQin (https://www.1stg.me)", 31 | "Christian Murphy " 32 | ], 33 | "type": "module", 34 | "sideEffects": false, 35 | "exports": "./index.js", 36 | "files": [ 37 | "lib/", 38 | "index.d.ts.map", 39 | "index.d.ts", 40 | "index.js" 41 | ], 42 | "dependencies": { 43 | "@types/mdx": "^2.0.0" 44 | }, 45 | "peerDependencies": { 46 | "@types/react": ">=16", 47 | "react": ">=16" 48 | }, 49 | "devDependencies": {}, 50 | "scripts": { 51 | "test": "npm run test-coverage", 52 | "test-api": "node --conditions development --loader=../../script/jsx-loader.js test/index.jsx", 53 | "test-coverage": "c8 --100 --reporter lcov npm run test-api" 54 | }, 55 | "xo": { 56 | "overrides": [ 57 | { 58 | "extends": "xo-react", 59 | "files": [ 60 | "**/*.jsx" 61 | ], 62 | "rules": { 63 | "react/jsx-no-bind": "off", 64 | "react/react-in-jsx-scope": "off" 65 | } 66 | } 67 | ], 68 | "prettier": true, 69 | "rules": { 70 | "n/file-extension-in-import": "off" 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/remark-mdx/index.js: -------------------------------------------------------------------------------- 1 | // Augment node types: 2 | /// 3 | 4 | /** 5 | * @typedef {import('./lib/index.js').Options} Options 6 | */ 7 | 8 | export {default} from './lib/index.js' 9 | -------------------------------------------------------------------------------- /packages/remark-mdx/lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @import {ToMarkdownOptions} from 'mdast-util-mdx' 3 | * @import {Options as MicromarkOptions} from 'micromark-extension-mdxjs' 4 | * @import {Processor} from 'unified' 5 | */ 6 | 7 | /** 8 | * @typedef {MicromarkOptions & ToMarkdownOptions} Options 9 | * Configuration. 10 | */ 11 | 12 | import {mdxFromMarkdown, mdxToMarkdown} from 'mdast-util-mdx' 13 | import {mdxjs} from 'micromark-extension-mdxjs' 14 | 15 | /** @type {Readonly} */ 16 | const emptyOptions = {} 17 | 18 | /** 19 | * Add support for MDX (JSX: `