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