diff --git a/.github/workflows/bb.yml b/.github/workflows/bb.yml new file mode 100644 index 000000000..3dbfce5b3 --- /dev/null +++ b/.github/workflows/bb.yml @@ -0,0 +1,13 @@ +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: unifiedjs/beep-boop-beta@main + with: + repo-token: ${{secrets.GITHUB_TOKEN}} +name: bb +on: + issues: + types: [closed, edited, labeled, opened, reopened, unlabeled] + pull_request_target: + types: [closed, edited, labeled, opened, reopened, unlabeled] diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a38018e5a..8f3edcb97 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,46 +1,46 @@ -name: main -on: - - pull_request - - push jobs: small: - name: test / ${{matrix.os}} / ${{matrix.node}} - runs-on: ${{matrix.os}} env: PUPPETEER_SKIP_DOWNLOAD: 1 + name: test / ${{matrix.os}} / ${{matrix.node}} + runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v4 with: - node-version: ${{matrix.node}} cache: npm + node-version: ${{matrix.node}} - run: npm ci - run: npm run test-api strategy: matrix: + include: + - node: lts/hydrogen + os: ubuntu-latest + node: + - node os: - macos-latest - windows-latest - node: - - node - include: - - os: ubuntu-latest - node: lts/gallium full: - name: full build - runs-on: ubuntu-latest env: PUPPETEER_SKIP_DOWNLOAD: 1 + name: full build + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v4 with: - node-version: node cache: npm + node-version: node - run: npm ci - run: npm test - uses: codecov/codecov-action@v4 +name: main +on: + - pull_request + - push diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 26bd71ba0..2d246af71 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -1,23 +1,18 @@ -name: website -on: - push: - branches: - - main jobs: deploy: - runs-on: ubuntu-latest - permissions: - contents: read - pages: write - id-token: write environment: name: Website url: ${{steps.deployment.outputs.page_url}} + permissions: + contents: read + id-token: write + pages: write + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: node - run: npm ci - run: npm run docs - uses: actions/upload-pages-artifact@v3 @@ -25,3 +20,8 @@ jobs: path: public - uses: actions/deploy-pages@v4 id: deployment +name: website +on: + push: + branches: + - main diff --git a/.gitignore b/.gitignore index 20c333c5c..355cc59ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ .DS_Store coverage/ node_modules/ -*.d.cts.map *.d.cts -*.d.ts.map *.d.ts +*.map +*.tsbuildinfo /public/ !/packages/mdx/lib/types.d.ts !/website/types.d.ts diff --git a/docs/_asset/editor.jsx b/docs/_asset/editor.jsx index c2939c7d6..99bd8d0ad 100644 --- a/docs/_asset/editor.jsx +++ b/docs/_asset/editor.jsx @@ -1,23 +1,21 @@ -/* - * @jsxRuntime automatic - * @jsxImportSource react - */ +/* @jsxRuntime automatic */ +/* @jsxImportSource react */ /** - * @typedef {import('@wooorm/starry-night').Grammar} Grammar - * @typedef {import('estree').Node} EstreeNode - * @typedef {import('estree').Program} Program - * @typedef {import('hast').Nodes} HastNodes - * @typedef {import('hast').Root} HastRoot - * @typedef {import('mdast').Nodes} MdastNodes - * @typedef {import('mdast').Root} MdastRoot - * @typedef {import('mdast-util-mdx-jsx').MdxJsxAttribute} MdxJsxAttribute - * @typedef {import('mdast-util-mdx-jsx').MdxJsxAttributeValueExpression} MdxJsxAttributeValueExpression - * @typedef {import('mdast-util-mdx-jsx').MdxJsxExpressionAttribute} MdxJsxExpressionAttribute - * @typedef {import('mdx/types.js').MDXModule} MDXModule - * @typedef {import('react-error-boundary').FallbackProps} FallbackProperties - * @typedef {import('unified').PluggableList} PluggableList - * @typedef {import('unist').Node} UnistNode + * @import {Grammar} from '@wooorm/starry-night' + * @import {Node as EstreeNode, Program} from 'estree' + * @import {Nodes as HastNodes, Root as HastRoot} from 'hast' + * @import {Nodes as MdastNodes, Root as MdastRoot} from 'mdast' + * @import { + MdxJsxAttribute, + MdxJsxAttributeValueExpression, + MdxJsxExpressionAttribute + * } from 'mdast-util-mdx-jsx' + * @import {MDXModule} from 'mdx/types.js' + * @import {ReactNode} from 'react' + * @import {FallbackProps} from 'react-error-boundary' + * @import {PluggableList} from 'unified' + * @import {Node as UnistNode} from 'unist' */ /** @@ -37,7 +35,7 @@ * OK. * @property {true} ok * Whether OK. - * @property {JSX.Element} value + * @property {ReactNode} value * Result. * * @typedef {EvalNok | EvalOk} EvalResult @@ -57,7 +55,6 @@ import textMd from '@wooorm/starry-night/text.md' import {visit as visitEstree} from 'estree-util-visit' import {toJsxRuntime} from 'hast-util-to-jsx-runtime' import {useEffect, useState} from 'react' -// @ts-expect-error: the automatic react runtime is untyped. import {Fragment, jsx, jsxs} from 'react/jsx-runtime' import ReactDom from 'react-dom/client' import {ErrorBoundary} from 'react-error-boundary' @@ -70,8 +67,6 @@ import {removePosition} from 'unist-util-remove-position' import {visit} from 'unist-util-visit' import {VFile} from 'vfile' -const runtime = {Fragment, jsx, jsxs} - const sample = `# Hello, world! Below is an example of markdown in JSX. @@ -142,7 +137,7 @@ function Playground() { const [frontmatter, setFrontmatter] = useState(false) const [gfm, setGfm] = useState(false) const [formatMarkdown, setFormatMarkdown] = useState(false) - const [jsx, setJsx] = useState(false) + const [generateJsx, setGenerateJsx] = useState(false) const [math, setMath] = useState(false) const [outputFormatFunctionBody, setOutputFormatFunctionBody] = useState(false) @@ -195,7 +190,7 @@ function Playground() { await compile(file, { development: show === 'result' ? false : development, - jsx: show === 'code' || show === 'esast' ? jsx : false, + jsx: show === 'code' || show === 'esast' ? generateJsx : false, outputFormat: show === 'result' || outputFormatFunctionBody ? 'function-body' @@ -208,7 +203,9 @@ function Playground() { if (show === 'result') { /** @type {MDXModule} */ const result = await run(String(file), { - ...runtime, + Fragment, + jsx, + jsxs, baseUrl: window.location.href }) @@ -231,7 +228,7 @@ function Playground() { JSON.stringify(ast, undefined, 2), 'source.json' ), - runtime + {Fragment, jsx, jsxs} )} @@ -242,10 +239,11 @@ function Playground() { return (
             
-              {toJsxRuntime(
-                starryNight.highlight(String(file), 'source.js'),
-                runtime
-              )}
+              {toJsxRuntime(starryNight.highlight(String(file), 'source.js'), {
+                Fragment,
+                jsx,
+                jsxs
+              })}
             
           
) @@ -298,7 +296,7 @@ function Playground() { directive, frontmatter, gfm, - jsx, + generateJsx, formatMarkdown, math, outputFormatFunctionBody, @@ -312,7 +310,7 @@ function Playground() { const scope = formatMarkdown ? 'text.md' : 'source.mdx' // Cast to actual value. const compiledResult = /** @type {EvalResult | undefined} */ (evalResult) - /** @type {JSX.Element | undefined} */ + /** @type {ReactNode | undefined} */ let display if (compiledResult) { @@ -334,7 +332,11 @@ function Playground() {
- {toJsxRuntime(starryNight.highlight(value, scope), runtime)} + {toJsxRuntime(starryNight.highlight(value, scope), { + Fragment, + jsx, + jsxs + })} {/* Trailing whitespace in a `textarea` is shown, but not in a `div` with `white-space: pre-wrap`. Add a `br` to make the last newline explicit. */} @@ -527,9 +529,9 @@ function Playground() { {' '} keep JSX (jsx: true) @@ -538,9 +540,9 @@ function Playground() { {' '} compile JSX away (jsx: false) @@ -570,9 +572,9 @@ function Playground() { /** * - * @param {Readonly} properties + * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ function ErrorFallback(properties) { @@ -592,7 +594,7 @@ function ErrorFallback(properties) { /** * @param {DisplayProperties} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ function DisplayError(properties) { @@ -642,6 +644,7 @@ function cleanUnistNode(node) { Array.isArray(node.attributes) ) { for (const attribute of node.attributes) { + removePosition(attribute, {force: true}) cleanUnistNode(attribute) } } @@ -659,9 +662,7 @@ function cleanUnistNode(node) { */ function removeFromEstree(node) { delete node.loc - // @ts-expect-error: this field is added by acorn. delete node.start - // @ts-expect-error: this field is added by acorn. delete node.end delete node.range } diff --git a/docs/_asset/index.css b/docs/_asset/index.css index 159461113..c336896ac 100644 --- a/docs/_asset/index.css +++ b/docs/_asset/index.css @@ -43,6 +43,30 @@ --hl: var(--blue-5); --fg: var(--black); --bg: var(--white); + + /* We manually add a blur on `::before` of the container, so no background needed. */ + --docsearch-container-background: transparent !important; + --docsearch-footer-background: var(--white) !important; + --docsearch-footer-shadow: 0 -1px 0 0 var(--gray-2) !important; + --docsearch-highlight-color: var(--hl) !important; + --docsearch-hit-color: var(--gray-7) !important; + --docsearch-hit-shadow: inset 0 0 0 1px var(--gray-2) !important; + /* This is actually used in a `background` field so does not have to be a gradient. */ + --docsearch-key-gradient: var(--gray-0) !important; + --docsearch-key-pressed-shadow: none !important; + --docsearch-key-shadow: inset 0 -1px 0 var(--gray-4) !important; + --docsearch-modal-background: var(--white) !important; + /* Card shadow: */ + --docsearch-modal-shadow: 0 0 0 0.2em rgb(3 102 214 / 0%), + 0 13px 27px -5px rgb(50 50 93 / 25%), 0 8px 16px -8px rgb(0 0 0 / 30%), + 0 -6px 16px -6px rgb(0 0 0 / 2.5%) !important; + /* Use the regular color: */ + --docsearch-muted-color: var(--black) !important; + --docsearch-primary-color: var(--hl) !important; + --docsearch-searchbox-background: var(--white) !important; + --docsearch-searchbox-focus-background: var(--gray-0) !important; + --docsearch-searchbox-shadow: inset 0 0 0 2px var(--hl) !important; + --docsearch-text-color: var(--black) !important; } * { @@ -297,7 +321,6 @@ pre code { } pre code, -.hljs, .frame-body, .frame-tab-item-selected { background-color: #fafafa !important; /* Color from one-light */ @@ -527,7 +550,8 @@ button.success { padding-top: calc(2 * (1em + 1ex)); } -.navigation::before { +.navigation::before, +.DocSearch-Container::before { content: ''; inset: 0; position: absolute; @@ -539,6 +563,10 @@ button.success { ); } +.DocSearch li { + margin: 0; +} + #banner { padding: calc(0.25 * (1em + 1ex)); background-color: var(--mdx-yellow); @@ -565,7 +593,8 @@ button.success { /* Note that the `backdrop-filter` itself is applied in light mode. */ @supports (backdrop-filter: blur(1ex)) { - .navigation::before { + .navigation::before, + .DocSearch-Container::before { backdrop-filter: saturate(200%) blur(1ex); background-image: radial-gradient( ellipse at 50% 0%, @@ -575,15 +604,16 @@ button.success { } } +.DocSearch-Modal, .navigation-primary, .navigation-secondary, +.navigation-search, .navigation-tertiary { z-index: 3; } .navigation .icon { display: block; - vertical-align: middle; width: auto; height: calc(1em + 1ex); } @@ -599,6 +629,7 @@ button.success { .navigation-primary, .navigation-secondary, +.navigation-search, .navigation-tertiary { margin: 0; padding: calc(0.5 * (1em + 1ex)); @@ -606,6 +637,14 @@ button.success { list-style-type: none; } +.navigation-search { + padding: calc(0.25em + 0.25ex); +} + +.DocSearch-Button { + margin: 0 !important; +} + .navigation-primary h1, .navigation-secondary li, .navigation-tertiary li { @@ -660,9 +699,6 @@ button.success { padding-inline-start: calc(0.5 * (1em + 1ex)); } -.nav-description { -} - .skip-to-navigation { inset-block-start: 0; inset-inline-start: 0; @@ -837,11 +873,13 @@ button.success { overflow-y: auto; } -.frame-tab-bar + pre { +.frame-tab-bar + pre, +.frame-tab-bar + .highlight > pre { margin-block-start: 0; } -.frame-tab-bar + pre code { +.frame-tab-bar + pre code, +.frame-tab-bar + .highlight > pre code { --squircle-radius: 0; border-top-left-radius: 0; border-top-right-radius: 0; @@ -946,6 +984,55 @@ details[open] { padding: calc(1em + 1ex); } +.rehype-twoslash-completion-deprecated { + opacity: 0.5; +} + +.rehype-twoslash-popover-target { + cursor: default; +} + +.highlight:is(:hover, :focus-within) .rehype-twoslash-popover-target { + background-color: var(--gray-2); +} + +/* Wavy underline for errors. */ +.rehype-twoslash-error-target { + background-repeat: repeat-x; + background-position: bottom left; + background-image: url('data:image/svg+xml,'); +} + +/* The content that will be shown in the tooltip. */ +.rehype-twoslash-popover { + position: absolute; + max-width: calc(45 * (1em + 1ex)); + padding: calc(0.5 * (1em + 1ex)); + margin: 0; + background-color: var(--bg); + border: 1px solid var(--gray-2); + border-radius: 3px; +} + +/* No padding if we have a padded code block (and perhaps more blocks) */ +.rehype-twoslash-popover:has(.rehype-twoslash-popover-code) { + padding: 0; +} + +.rehype-twoslash-popover-code { + margin: 0; +} + +.rehype-twoslash-popover-code > code { + mask-image: none; + border-radius: 0; +} + +.rehype-twoslash-popover-description { + background-color: var(--bg); + padding: 0 1em; +} + @media (prefers-color-scheme: dark) { :root { --white: #f0f6fc; @@ -963,6 +1050,8 @@ details[open] { --hl: var(--mdx-yellow); --fg: var(--white); --bg: var(--black); + + --docsearch-key-gradient: #eaeef2 !important; } .navigation-secondary a { @@ -1038,6 +1127,14 @@ details[open] { background-color: var(--gray-8); } + .highlight:is(:hover, :focus-within) .rehype-twoslash-popover-target { + background-color: var(--gray-5); + } + + .rehype-twoslash-popover { + border-color: var(--gray-6); + } + h6 { color: var(--gray-3); } @@ -1062,7 +1159,6 @@ details[open] { } pre code, - .hljs, .frame-body, .frame-tab-item-selected, .frame-tab-item-dark.frame-tab-item-selected { @@ -1148,6 +1244,10 @@ details[open] { font-size: 1.125em; } + .navigation-search { + padding: calc(0.3em + 0.3ex); + } + #markdown-for-thecomponent-era { font-size: 3rem; line-height: calc(1em + (1 / 3 * 1ex)); @@ -1213,7 +1313,7 @@ details[open] { @media (min-width: 76em) { #markdown-for-thecomponent-era { - font-size: 6rem; + font-size: 5.9rem; line-height: calc(1em + (1 / 6 * 1ex)); margin-block: calc(1 / 6 * (1em + 1ex)); } @@ -1231,23 +1331,6 @@ details[open] { } } -/* Fix a11y. */ -.hljs-built_in, -.hljs-symbol { - color: hsl(24deg 92% 40%) !important; -} - -@media (prefers-color-scheme: dark) { - .hljs-section { - color: #488bef !important; - } - - .hljs-built_in, - .hljs-symbol { - color: #ffa657 !important; - } -} - .playground { min-height: 40rem; gap: calc(1em + 1ex); diff --git a/docs/_asset/index.js b/docs/_asset/index.js index 2b4f9e2a1..3346cd828 100644 --- a/docs/_asset/index.js +++ b/docs/_asset/index.js @@ -1,8 +1,12 @@ +/* eslint-disable unicorn/prefer-query-selector */ /// +import docsearch_ from '@docsearch/js' +import {computePosition, shift} from '@floating-ui/dom' import copyToClipboard from 'copy-to-clipboard' import {ok as assert} from 'devlop' +// Squircles. if ('paintWorklet' in CSS) { // @ts-expect-error: TS doesn’t understand Houdini. CSS.paintWorklet.addModule( @@ -10,6 +14,7 @@ if ('paintWorklet' in CSS) { ) } +// Copy buttons. const copies = Array.from(document.querySelectorAll('button.copy-button')) const copyTemplate = document.createElement('template') const copiedTemplate = document.createElement('template') @@ -57,7 +62,35 @@ for (const copy of copies) { assert(copy instanceof HTMLButtonElement) copy.type = 'button' copy.replaceChildren(copyIcon.cloneNode(true)) - copy.addEventListener('click', onclick) + copy.addEventListener('click', oncopyonclick) +} + +const popoverTargets = /** @type {Array} */ ( + Array.from(document.querySelectorAll('.rehype-twoslash-popover-target')) +) + +for (const popoverTarget of popoverTargets) { + /** @type {NodeJS.Timeout | number} */ + let timeout = 0 + + popoverTarget.addEventListener('click', function () { + popoverShow(popoverTarget) + }) + + popoverTarget.addEventListener('mouseenter', function () { + clearTimeout(timeout) + timeout = setTimeout(function () { + popoverShow(popoverTarget) + }, 300) + }) + + popoverTarget.addEventListener('mouseleave', function () { + clearTimeout(timeout) + }) + + if (popoverTarget.classList.contains('rehype-twoslash-autoshow')) { + popoverShow(popoverTarget) + } } /** @@ -66,7 +99,7 @@ for (const copy of copies) { * @returns {undefined} * Nothing. */ -function onclick() { +function oncopyonclick() { assert(copyIcon) assert(copiedIcon) assert(this instanceof HTMLButtonElement) @@ -84,3 +117,44 @@ function onclick() { this.replaceChildren(copyIcon.cloneNode(true)) }, 2000) } + +/** + * @param {HTMLElement} popoverTarget + * Popover target. + * @returns {undefined} + * Nothing. + */ +function popoverShow(popoverTarget) { + const id = popoverTarget.dataset.popoverTarget + if (!id) return + const popover = document.getElementById(id) + if (!popover) return + + popover.showPopover() + + computePosition(popoverTarget, popover, { + placement: 'bottom', + middleware: [shift({padding: 5})] + }).then( + /** + * @param {{x: number, y: number}} value + */ + function (value) { + popover.style.left = value.x + 'px' + popover.style.top = value.y + 'px' + } + ) +} + +// Docsearch. +// Note: types are wrong. +const docsearch = /** @type {import('@docsearch/js')['default']} */ ( + /** @type {unknown} */ (docsearch_) +) + +docsearch({ + appId: 'B0O9AAZ9L2', + apiKey: '71f38eae605e3e6d500368617e32c19f', + container: '#docsearch', + indexName: 'mdxjs' +}) diff --git a/docs/_component/blog.jsx b/docs/_component/blog.jsx index ff2b728fe..f6aba62b9 100644 --- a/docs/_component/blog.jsx +++ b/docs/_component/blog.jsx @@ -1,5 +1,6 @@ /** - * @typedef {import('./sort.js').Item} Item + * @import {ReactNode} from 'react' + * @import {Item} from './sort.js' */ /** @@ -21,18 +22,15 @@ import {apStyleTitleCase} from 'ap-style-title-case' import {toJsxRuntime} from 'hast-util-to-jsx-runtime' import React from 'react' -// @ts-expect-error: the automatic react runtime is untyped. import {Fragment, jsx, jsxs} from 'react/jsx-runtime' import {sortItems} from './sort.js' -const runtime = {Fragment, jsx, jsxs} - const dateTimeFormat = new Intl.DateTimeFormat('en', {dateStyle: 'long'}) /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function BlogEntry(properties) { @@ -69,7 +67,7 @@ export function BlogEntry(properties) {
{meta.descriptionHast ? ( - toJsxRuntime(meta.descriptionHast, runtime) + toJsxRuntime(meta.descriptionHast, {Fragment, jsx, jsxs}) ) : description ? (

{description}

) : undefined} @@ -108,7 +106,7 @@ export function BlogEntry(properties) { /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function BlogGroup(properties) { diff --git a/docs/_component/home.jsx b/docs/_component/home.jsx index 949cead3d..841798464 100644 --- a/docs/_component/home.jsx +++ b/docs/_component/home.jsx @@ -1,11 +1,11 @@ /** - * @typedef {import('react').ReactNode} ReactNode - * @typedef {import('vfile').Data['meta']} DataMeta - * @typedef {import('./sort.js').Item} Item + * @import {ReactNode} from 'react' + * @import {Data} from 'vfile' + * @import {Item} from './sort.js' */ /** - * @typedef {Exclude} Meta + * @typedef {Exclude} Meta * * @typedef Properties * Properties. @@ -26,7 +26,7 @@ import {NavigationSite, NavigationSiteSkip} from './nav-site.jsx' /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function Home(properties) { diff --git a/docs/_component/layout.jsx b/docs/_component/layout.jsx index 6e4694520..8073a5464 100644 --- a/docs/_component/layout.jsx +++ b/docs/_component/layout.jsx @@ -1,6 +1,7 @@ /** - * @typedef {import('vfile').Data['meta']} DataMeta - * @typedef {import('./sort.js').Item} Item + * @import {ReactNode} from 'react' + * @import {Data} from 'vfile' + * @import {Item} from './sort.js' */ /** @@ -10,11 +11,11 @@ * Name. * @property {Readonly} ghUrl * GitHub URL. - * @property {Readonly | undefined} [meta] + * @property {Readonly | undefined} [meta] * Meta. * @property {Readonly} navigationTree * Navigation tree. - * @property {JSX.Element} children + * @property {ReactNode} children * Children. */ @@ -28,7 +29,7 @@ const dateTimeFormat = new Intl.DateTimeFormat('en', {dateStyle: 'long'}) /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function Layout(properties) { diff --git a/docs/_component/nav-site.jsx b/docs/_component/nav-site.jsx index 06d47f7ef..c29ca757d 100644 --- a/docs/_component/nav-site.jsx +++ b/docs/_component/nav-site.jsx @@ -1,5 +1,6 @@ /** - * @typedef {import('./sort.js').Item} Item + * @import {ReactNode} from 'react' + * @import {Item} from './sort.js' */ /** @@ -34,7 +35,7 @@ export function NavigationSiteSkip() { /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function NavigationSite(properties) { @@ -57,6 +58,9 @@ export function NavigationSite(properties) {
+
+
+
/** - * @typedef {import('hast').ElementContent} ElementContent - * @typedef {import('react').ReactNode} ReactNode - * @typedef {import('./sort.js').Item} Item + * @import {ElementContent} from 'hast' + * @import {ReactNode} from 'react' + * @import {Item} from './sort.js' */ /** @@ -38,18 +38,15 @@ import {apStyleTitleCase} from 'ap-style-title-case' import {toJsxRuntime} from 'hast-util-to-jsx-runtime' import React from 'react' -// @ts-expect-error: the automatic react runtime is untyped. import {Fragment, jsx, jsxs} from 'react/jsx-runtime' import {sortItems} from './sort.js' -const runtime = {Fragment, jsx, jsxs} - const dateTimeFormat = new Intl.DateTimeFormat('en', {dateStyle: 'long'}) /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function NavigationGroup(properties) { @@ -72,7 +69,7 @@ export function NavigationGroup(properties) { /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function NavigationItem(properties) { @@ -107,7 +104,7 @@ export function NavigationItem(properties) { properties: {className: ['nav-description']}, children }, - runtime + {Fragment, jsx, jsxs} ) } else { description = matter.description || meta.description || undefined diff --git a/docs/_component/note.jsx b/docs/_component/note.jsx index dae864c83..49169df4b 100644 --- a/docs/_component/note.jsx +++ b/docs/_component/note.jsx @@ -1,5 +1,5 @@ /** - * @typedef {import('react').ReactNode} ReactNode + * @import {ReactNode} from 'react' */ /** @@ -22,7 +22,7 @@ const known = new Set(['info', 'legacy', 'important']) /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function Note(properties) { diff --git a/docs/_component/snowfall.jsx b/docs/_component/snowfall.jsx index d6d60077e..012f34527 100644 --- a/docs/_component/snowfall.jsx +++ b/docs/_component/snowfall.jsx @@ -1,3 +1,7 @@ +/** + * @import {ReactNode} from 'react' + */ + /** * @typedef Properties * Properties. @@ -14,7 +18,7 @@ const data = [6, 5, 2, 4.5, 1.5, 2.5, 2, 2.5, 1.5, 2.5, 3.5, 7] /** * @param {Readonly} properties * Properties. - * @returns {JSX.Element} + * @returns {ReactNode} * Element. */ export function Chart(properties) { diff --git a/docs/_component/sort.js b/docs/_component/sort.js index 780b15ab6..e5bc524a9 100644 --- a/docs/_component/sort.js +++ b/docs/_component/sort.js @@ -1,5 +1,5 @@ /** - * @typedef {import('vfile').Data} Data + * @import {Data} from 'vfile' */ /** diff --git a/docs/blog/index.mdx b/docs/blog/index.mdx index 984c09414..e3053ac0e 100644 --- a/docs/blog/index.mdx +++ b/docs/blog/index.mdx @@ -1,9 +1,20 @@ +{ + /** + * @import {Item} from '../_component/sort.js' + */ + + /** + * @typedef Props + * @property {Item} navigationTree + */ +} + import assert from 'node:assert/strict' import {BlogGroup} from '../_component/blog.jsx' export const info = { author: [{name: 'MDX Contributors'}], - modified: new Date('2021-11-01'), + modified: new Date('2024-07-04'), published: new Date('2021-11-01') } export const navExcludeGroup = true @@ -16,11 +27,6 @@ The latest news about MDX. { (function () { - /** - * @typedef {import('../_component/sort.js').Item} Item - */ - - /** @type {Item} */ const navigationTree = props.navigationTree const category = navigationTree.children.find(function (item) { return item.name === '/blog/' diff --git a/docs/blog/v3.mdx b/docs/blog/v3.mdx index 2a50c1845..89279eef8 100644 --- a/docs/blog/v3.mdx +++ b/docs/blog/v3.mdx @@ -58,9 +58,7 @@ technically breaking. We now accept block expressions right next to block JSX tags: -{/* Note: `language` because our theme doesn’t support it yet. */} - -```jsx chrome=no language="mdx" +```mdx chrome=no