1
1
mirror of https://github.com/mdx-js/mdx.git synced 2024-09-11 15:05:32 +03:00

Refactor some more

This commit is contained in:
Titus Wormer 2023-10-18 16:32:48 +02:00
parent a362bb43ee
commit 780fa02778
No known key found for this signature in database
GPG Key ID: E6E581152ED04E2E
12 changed files with 468 additions and 411 deletions

View File

@ -322,7 +322,7 @@ function Playground() {
return ( return (
<> <>
<form className="playground-editor"> <form>
<div className="playground-area"> <div className="playground-area">
<div className="playground-inner"> <div className="playground-inner">
<div className="playground-draw"> <div className="playground-draw">

View File

@ -1205,15 +1205,6 @@ button.success {
} }
} }
/* To do: clean from here */
.playground {
display: grid;
grid-template-columns: 49% 49%;
min-height: 40rem;
gap: calc(1em + 1ex);
margin-inline: calc(1em + 1ex);
}
.playground-area, .playground-area,
.playground-controls, .playground-controls,
.playground-result { .playground-result {

23
package-lock.json generated
View File

@ -27,6 +27,8 @@
"@types/mdx": "^2.0.0", "@types/mdx": "^2.0.0",
"@types/react": "^18.0.0", "@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0", "@types/react-dom": "^18.0.0",
"@types/ungap__structured-clone": "^0.3.0",
"@ungap/structured-clone": "^1.0.0",
"@vue/babel-plugin-jsx": "^1.0.0", "@vue/babel-plugin-jsx": "^1.0.0",
"@vue/server-renderer": "^3.0.0", "@vue/server-renderer": "^3.0.0",
"@wooorm/starry-night": "^3.0.0", "@wooorm/starry-night": "^3.0.0",
@ -47,7 +49,10 @@
"eslint-plugin-react-hooks": "^4.0.0", "eslint-plugin-react-hooks": "^4.0.0",
"estree-util-value-to-estree": "^3.0.0", "estree-util-value-to-estree": "^3.0.0",
"globby": "^13.0.0", "globby": "^13.0.0",
"hast-util-from-html": "^2.0.0",
"hast-util-sanitize": "^5.0.0",
"hast-util-select": "^6.0.0", "hast-util-select": "^6.0.0",
"hast-util-to-html": "^9.0.0",
"hast-util-to-jsx-runtime": "^2.0.0", "hast-util-to-jsx-runtime": "^2.0.0",
"hast-util-to-text": "^4.0.0", "hast-util-to-text": "^4.0.0",
"hastscript": "^8.0.0", "hastscript": "^8.0.0",
@ -74,7 +79,6 @@
"rehype-preset-minify": "^7.0.0", "rehype-preset-minify": "^7.0.0",
"rehype-raw": "^7.0.0", "rehype-raw": "^7.0.0",
"rehype-remove-comments": "^6.0.0", "rehype-remove-comments": "^6.0.0",
"rehype-sanitize": "^6.0.0",
"rehype-shift-heading": "^2.0.0", "rehype-shift-heading": "^2.0.0",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"rehype-stringify": "^10.0.0", "rehype-stringify": "^10.0.0",
@ -95,8 +99,7 @@
"rollup": "^4.0.0", "rollup": "^4.0.0",
"type-coverage": "^2.0.0", "type-coverage": "^2.0.0",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"unified": "^11.0.3", "unified": "^11.0.0",
"unist-builder": "^4.0.0",
"unist-util-remove-position": "^5.0.0", "unist-util-remove-position": "^5.0.0",
"unist-util-visit": "^5.0.0", "unist-util-visit": "^5.0.0",
"vfile": "^6.0.0", "vfile": "^6.0.0",
@ -13532,20 +13535,6 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/rehype-sanitize": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz",
"integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==",
"dev": true,
"dependencies": {
"@types/hast": "^3.0.0",
"hast-util-sanitize": "^5.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/rehype-shift-heading": { "node_modules/rehype-shift-heading": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/rehype-shift-heading/-/rehype-shift-heading-2.0.0.tgz", "resolved": "https://registry.npmjs.org/rehype-shift-heading/-/rehype-shift-heading-2.0.0.tgz",

View File

@ -31,6 +31,8 @@
"@types/mdx": "^2.0.0", "@types/mdx": "^2.0.0",
"@types/react": "^18.0.0", "@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0", "@types/react-dom": "^18.0.0",
"@types/ungap__structured-clone": "^0.3.0",
"@ungap/structured-clone": "^1.0.0",
"@vue/babel-plugin-jsx": "^1.0.0", "@vue/babel-plugin-jsx": "^1.0.0",
"@vue/server-renderer": "^3.0.0", "@vue/server-renderer": "^3.0.0",
"@wooorm/starry-night": "^3.0.0", "@wooorm/starry-night": "^3.0.0",
@ -51,7 +53,10 @@
"eslint-plugin-react-hooks": "^4.0.0", "eslint-plugin-react-hooks": "^4.0.0",
"estree-util-value-to-estree": "^3.0.0", "estree-util-value-to-estree": "^3.0.0",
"globby": "^13.0.0", "globby": "^13.0.0",
"hast-util-from-html": "^2.0.0",
"hast-util-sanitize": "^5.0.0",
"hast-util-select": "^6.0.0", "hast-util-select": "^6.0.0",
"hast-util-to-html": "^9.0.0",
"hast-util-to-jsx-runtime": "^2.0.0", "hast-util-to-jsx-runtime": "^2.0.0",
"hast-util-to-text": "^4.0.0", "hast-util-to-text": "^4.0.0",
"hastscript": "^8.0.0", "hastscript": "^8.0.0",
@ -78,7 +83,6 @@
"rehype-preset-minify": "^7.0.0", "rehype-preset-minify": "^7.0.0",
"rehype-raw": "^7.0.0", "rehype-raw": "^7.0.0",
"rehype-remove-comments": "^6.0.0", "rehype-remove-comments": "^6.0.0",
"rehype-sanitize": "^6.0.0",
"rehype-shift-heading": "^2.0.0", "rehype-shift-heading": "^2.0.0",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"rehype-stringify": "^10.0.0", "rehype-stringify": "^10.0.0",
@ -99,8 +103,7 @@
"rollup": "^4.0.0", "rollup": "^4.0.0",
"type-coverage": "^2.0.0", "type-coverage": "^2.0.0",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"unified": "^11.0.3", "unified": "^11.0.0",
"unist-builder": "^4.0.0",
"unist-util-remove-position": "^5.0.0", "unist-util-remove-position": "^5.0.0",
"unist-util-visit": "^5.0.0", "unist-util-visit": "^5.0.0",
"vfile": "^6.0.0", "vfile": "^6.0.0",

View File

@ -1,5 +1,6 @@
/** /**
* @typedef {import('node:module').Module} Module * @typedef {import('node:module').Module} Module
* @typedef {import('mdx/types.js').MDXModule} MDXModule
*/ */
/** /**
@ -67,8 +68,7 @@ function register(options) {
*/ */
function mdx(module, path) { function mdx(module, path) {
const file = processSync(fs.readFileSync(path)) const file = processSync(fs.readFileSync(path))
// To do: type `run`. /** @type {MDXModule} */
/** @type {{default: unknown}} */
const result = runSync(file, runtime) const result = runSync(file, runtime)
// Something going weird here w/ `type-coverage`. // Something going weird here w/ `type-coverage`.
// type-coverage:ignore-next-line // type-coverage:ignore-next-line

View File

@ -11,7 +11,6 @@ import remarkMdx from 'remark-mdx'
import remarkParse from 'remark-parse' import remarkParse from 'remark-parse'
import remarkStringify from 'remark-stringify' import remarkStringify from 'remark-stringify'
import {unified} from 'unified' import {unified} from 'unified'
import {u} from 'unist-builder'
import {removePosition} from 'unist-util-remove-position' import {removePosition} from 'unist-util-remove-position'
import {visit} from 'unist-util-visit' import {visit} from 'unist-util-visit'
@ -31,16 +30,24 @@ test('remark-mdx: parse', async function (t) {
clean(tree) clean(tree)
assert.deepEqual( assert.deepEqual(tree, {
tree, type: 'root',
u('root', [ children: [
u('paragraph', [ {
u('text', 'Alpha '), type: 'paragraph',
u('mdxJsxTextElement', {name: 'b', attributes: []}, []), children: [
u('text', ' charlie.') {type: 'text', value: 'Alpha '},
]) {
]) type: 'mdxJsxTextElement',
) name: 'b',
attributes: [],
children: []
},
{type: 'text', value: ' charlie.'}
]
}
]
})
}) })
await t.test('should parse an opening and a closing tag', function () { await t.test('should parse an opening and a closing tag', function () {
@ -48,16 +55,24 @@ test('remark-mdx: parse', async function (t) {
clean(tree) clean(tree)
assert.deepEqual( assert.deepEqual(tree, {
tree, type: 'root',
u('root', [ children: [
u('paragraph', [ {
u('text', 'Alpha '), type: 'paragraph',
u('mdxJsxTextElement', {name: 'b', attributes: []}, []), children: [
u('text', ' charlie.') {type: 'text', value: 'Alpha '},
]) {
]) type: 'mdxJsxTextElement',
) name: 'b',
attributes: [],
children: []
},
{type: 'text', value: ' charlie.'}
]
}
]
})
}) })
await t.test('should parse fragments', function () { await t.test('should parse fragments', function () {
@ -65,16 +80,24 @@ test('remark-mdx: parse', async function (t) {
clean(tree) clean(tree)
assert.deepEqual( assert.deepEqual(tree, {
tree, type: 'root',
u('root', [ children: [
u('paragraph', [ {
u('text', 'Alpha '), type: 'paragraph',
u('mdxJsxTextElement', {name: null, attributes: []}, []), children: [
u('text', ' charlie.') {type: 'text', value: 'Alpha '},
]) {
]) type: 'mdxJsxTextElement',
) name: null,
attributes: [],
children: []
},
{type: 'text', value: ' charlie.'}
]
}
]
})
}) })
await t.test('should parse markdown inside tags', function () { await t.test('should parse markdown inside tags', function () {
@ -82,18 +105,26 @@ test('remark-mdx: parse', async function (t) {
clean(tree) clean(tree)
assert.deepEqual( assert.deepEqual(tree, {
tree, type: 'root',
u('root', [ children: [
u('paragraph', [ {
u('text', 'Alpha '), type: 'paragraph',
u('mdxJsxTextElement', {name: 'b', attributes: []}, [ children: [
u('emphasis', [u('text', 'bravo')]) {type: 'text', value: 'Alpha '},
]), {
u('text', ' charlie.') type: 'mdxJsxTextElement',
]) name: 'b',
]) attributes: [],
) children: [
{type: 'emphasis', children: [{type: 'text', value: 'bravo'}]}
]
},
{type: 'text', value: ' charlie.'}
]
}
]
})
}) })
await t.test('should parse expressions', function () { await t.test('should parse expressions', function () {
@ -101,16 +132,23 @@ test('remark-mdx: parse', async function (t) {
clean(tree) clean(tree)
assert.deepEqual( assert.deepEqual(tree, {
tree, type: 'root',
u('root', [ children: [
u('paragraph', [ {
u('text', 'Alpha '), type: 'paragraph',
u('mdxTextExpression', {data: {estree: undefined}}, '1 + 1'), children: [
u('text', ' charlie.') {type: 'text', value: 'Alpha '},
]) {
]) type: 'mdxTextExpression',
) data: {estree: undefined},
value: '1 + 1'
},
{type: 'text', value: ' charlie.'}
]
}
]
})
}) })
}) })
@ -119,93 +157,124 @@ test('remark-mdx: stringify', async function (t) {
await t.test('should serialize an empty nameless fragment', function () { await t.test('should serialize an empty nameless fragment', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u('mdxJsxTextElement', {name: null, attributes: []}, []), type: 'paragraph',
u('text', ' charlie.') children: [
]) {type: 'text', value: 'Alpha '},
]) {
), type: 'mdxJsxTextElement',
name: null,
attributes: [],
children: []
},
{type: 'text', value: ' charlie.'}
]
}
]
}),
'Alpha <></> charlie.\n' 'Alpha <></> charlie.\n'
) )
}) })
await t.test('should serialize a tag with a name', function () { await t.test('should serialize a tag with a name', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u('mdxJsxTextElement', {name: 'b', attributes: []}, []), type: 'paragraph',
u('text', ' charlie.') children: [
]) {type: 'text', value: 'Alpha '},
]) {
), type: 'mdxJsxTextElement',
name: 'b',
attributes: [],
children: []
},
{type: 'text', value: ' charlie.'}
]
}
]
}),
'Alpha <b /> charlie.\n' 'Alpha <b /> charlie.\n'
) )
}) })
await t.test('should serialize a boolean attribute (element)', function () { await t.test('should serialize a boolean attribute (element)', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u( type: 'paragraph',
'mdxJsxTextElement', children: [
{name: 'b', attributes: [u('mdxJsxAttribute', {name: 'bravo'})]}, {type: 'text', value: 'Alpha '},
[] {
), type: 'mdxJsxTextElement',
u('text', ' charlie.') name: 'b',
]) attributes: [{type: 'mdxJsxAttribute', name: 'bravo'}],
]) children: []
), },
{type: 'text', value: ' charlie.'}
]
}
]
}),
'Alpha <b bravo /> charlie.\n' 'Alpha <b bravo /> charlie.\n'
) )
}) })
await t.test('should serialize an attribute (element)', function () { await t.test('should serialize an attribute (element)', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u( type: 'paragraph',
'mdxJsxTextElement', children: [
{type: 'text', value: 'Alpha '},
{ {
type: 'mdxJsxTextElement',
name: 'b', name: 'b',
attributes: [u('mdxJsxAttribute', {name: 'bravo'}, 'bravo')] attributes: [
{type: 'mdxJsxAttribute', name: 'bravo', value: 'bravo'}
],
children: []
}, },
[] {type: 'text', value: ' charlie.'}
), ]
u('text', ' charlie.') }
]) ]
]) }),
),
'Alpha <b bravo="bravo" /> charlie.\n' 'Alpha <b bravo="bravo" /> charlie.\n'
) )
}) })
await t.test('should serialize a prefixed attribute (element)', function () { await t.test('should serialize a prefixed attribute (element)', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u( type: 'paragraph',
'mdxJsxTextElement', children: [
{type: 'text', value: 'Alpha '},
{ {
type: 'mdxJsxTextElement',
name: 'b', name: 'b',
attributes: [u('mdxJsxAttribute', {name: 'br:avo'}, 'bravo')] attributes: [
{type: 'mdxJsxAttribute', name: 'br:avo', value: 'bravo'}
],
children: []
}, },
[] {type: 'text', value: ' charlie.'}
), ]
u('text', ' charlie.') }
]) ]
]) }),
),
'Alpha <b br:avo="bravo" /> charlie.\n' 'Alpha <b br:avo="bravo" /> charlie.\n'
) )
}) })
@ -214,22 +283,27 @@ test('remark-mdx: stringify', async function (t) {
'should serialize a attribute expression (element)', 'should serialize a attribute expression (element)',
function () { function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u( type: 'paragraph',
'mdxJsxTextElement', children: [
{type: 'text', value: 'Alpha '},
{ {
type: 'mdxJsxTextElement',
name: 'b', name: 'b',
attributes: [u('mdxJsxExpressionAttribute', '...props')] attributes: [
{type: 'mdxJsxExpressionAttribute', value: '...props'}
],
children: []
}, },
[] {type: 'text', value: ' charlie.'}
), ]
u('text', ' charlie.') }
]) ]
]) }),
),
'Alpha <b {...props} /> charlie.\n' 'Alpha <b {...props} /> charlie.\n'
) )
} }
@ -239,31 +313,35 @@ test('remark-mdx: stringify', async function (t) {
'should serialize an expression attribute (element)', 'should serialize an expression attribute (element)',
function () { function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u( type: 'paragraph',
'mdxJsxTextElement', children: [
{type: 'text', value: 'Alpha '},
{ {
type: 'mdxJsxTextElement',
name: 'b', name: 'b',
attributes: [ attributes: [
u('mdxJsxAttribute', { {
type: 'mdxJsxAttribute',
name: 'b', name: 'b',
value: u( value: {
'mdxJsxAttributeValueExpression', type: 'mdxJsxAttributeValueExpression',
{data: {estree: undefined}}, data: {estree: undefined},
'1 + 1' value: '1 + 1'
) }
}) }
] ],
children: []
}, },
[] {type: 'text', value: ' charlie.'}
), ]
u('text', ' charlie.') }
]) ]
]) }),
),
'Alpha <b b={1 + 1} /> charlie.\n' 'Alpha <b b={1 + 1} /> charlie.\n'
) )
} }
@ -271,79 +349,107 @@ test('remark-mdx: stringify', async function (t) {
await t.test('should write out a url as the raw value', function () { await t.test('should write out a url as the raw value', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('link', {url: 'https://mdxjs.com'}, [ {
u('text', 'https://mdxjs.com') type: 'paragraph',
]) children: [
]) {
]) type: 'link',
), url: 'https://mdxjs.com',
children: [{type: 'text', value: 'https://mdxjs.com'}]
}
]
}
]
}),
'[https://mdxjs.com](https://mdxjs.com)\n' '[https://mdxjs.com](https://mdxjs.com)\n'
) )
}) })
await t.test('should encode `<` in text', function () { await t.test('should encode `<` in text', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u('mdxJsxTextElement', {name: null, attributes: []}, [ type: 'paragraph',
u('text', '1 < 3') children: [
]), {type: 'text', value: 'Alpha '},
u('text', ' bravo.') {
]) type: 'mdxJsxTextElement',
]) name: null,
), attributes: [],
children: [{type: 'text', value: '1 < 3'}]
},
{type: 'text', value: ' bravo.'}
]
}
]
}),
'Alpha <>1 \\< 3</> bravo.\n' 'Alpha <>1 \\< 3</> bravo.\n'
) )
}) })
await t.test('should encode `{` in text', function () { await t.test('should encode `{` in text', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u('mdxJsxTextElement', {name: null, attributes: []}, [ type: 'paragraph',
u('text', '1 { 3') children: [
]), {type: 'text', value: 'Alpha '},
u('text', ' bravo.') {
]) type: 'mdxJsxTextElement',
]) name: null,
), attributes: [],
children: [{type: 'text', value: '1 { 3'}]
},
{type: 'text', value: ' bravo.'}
]
}
]
}),
'Alpha <>1 \\{ 3</> bravo.\n' 'Alpha <>1 \\{ 3</> bravo.\n'
) )
}) })
await t.test('should serialize empty expressions', function () { await t.test('should serialize empty expressions', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u('mdxTextExpression', ''), type: 'paragraph',
u('text', ' bravo.') children: [
]) {type: 'text', value: 'Alpha '},
]) {type: 'mdxTextExpression', value: ''},
), {type: 'text', value: ' bravo.'}
]
}
]
}),
'Alpha {} bravo.\n' 'Alpha {} bravo.\n'
) )
}) })
await t.test('should serialize expressions', function () { await t.test('should serialize expressions', function () {
assert.equal( assert.equal(
basic.stringify( basic.stringify({
u('root', [ type: 'root',
u('paragraph', [ children: [
u('text', 'Alpha '), {
u('mdxTextExpression', '1 + 1'), type: 'paragraph',
u('text', ' bravo.') children: [
]) {type: 'text', value: 'Alpha '},
]) {type: 'mdxTextExpression', value: '1 + 1'},
), {type: 'text', value: ' bravo.'}
]
}
]
}),
'Alpha {1 + 1} bravo.\n' 'Alpha {1 + 1} bravo.\n'
) )
}) })

View File

@ -3,176 +3,176 @@
"trailingSlash": true, "trailingSlash": true,
"redirects": [ "redirects": [
{ {
"source": "/about/", "destination": "/community/about/",
"destination": "/community/about/" "source": "/about/"
}, },
{ {
"source": "/advanced/", "destination": "/guides/",
"destination": "/guides/" "source": "/advanced/"
}, },
{ {
"source": "/advanced/api/", "destination": "/packages/mdx/#api",
"destination": "/packages/mdx/#api" "source": "/advanced/api/"
}, },
{ {
"source": "/advanced/ast/", "destination": "/packages/remark-mdx/#syntax-tree",
"destination": "/packages/remark-mdx/#syntax-tree" "source": "/advanced/ast/"
}, },
{ {
"source": "/advanced/components/", "destination": "/docs/using-mdx/",
"destination": "/docs/using-mdx/" "source": "/advanced/components/"
}, },
{ {
"source": "/advanced/contributing/", "destination": "/community/contribute/",
"destination": "/community/contribute/" "source": "/advanced/contributing/"
}, },
{ {
"source": "/advanced/custom-loader/", "destination": "/guides/frontmatter/",
"destination": "/guides/frontmatter/" "source": "/advanced/custom-loader/"
}, },
{ {
"source": "/advanced/retext-plugins/", "destination": "/docs/extending-mdx/#using-plugins",
"destination": "/docs/extending-mdx/#using-plugins" "source": "/advanced/retext-plugins/"
}, },
{ {
"source": "/advanced/plugins/", "destination": "/docs/extending-mdx/",
"destination": "/docs/extending-mdx/" "source": "/advanced/plugins/"
}, },
{ {
"source": "/advanced/runtime/", "destination": "/packages/mdx/#evaluatefile-options",
"destination": "/packages/mdx/#evaluatefile-options" "source": "/advanced/runtime/"
}, },
{ {
"source": "/advanced/specification/", "destination": "/packages/remark-mdx/#syntax-tree",
"destination": "/packages/remark-mdx/#syntax-tree" "source": "/advanced/specification/"
}, },
{ {
"source": "/advanced/sync-api/", "destination": "/packages/mdx/#api",
"destination": "/packages/mdx/#api" "source": "/advanced/sync-api/"
}, },
{ {
"source": "/advanced/transform-content/", "destination": "/packages/remark-mdx/",
"destination": "/packages/remark-mdx/" "source": "/advanced/transform-content/"
}, },
{ {
"source": "/advanced/typescript/", "destination": "/docs/getting-started/#types",
"destination": "/docs/getting-started/#types" "source": "/advanced/typescript/"
}, },
{ {
"source": "/advanced/writing-a-plugin/", "destination": "/guides/frontmatter/",
"destination": "/guides/frontmatter/" "source": "/advanced/writing-a-plugin/"
}, },
{ {
"source": "/contributing/", "destination": "/community/contribute/",
"destination": "/community/contribute/" "source": "/contributing/"
}, },
{ {
"source": "/editor-plugins/", "destination": "/docs/getting-started/#editor",
"destination": "/docs/getting-started/#editor" "source": "/editor-plugins/"
}, },
{ {
"source": "/editors/", "destination": "/docs/getting-started/#editor",
"destination": "/docs/getting-started/#editor" "source": "/editors/"
}, },
{ {
"source": "/getting-started/create-react-app/", "destination": "/docs/getting-started/#create-react-app-cra",
"destination": "/docs/getting-started/#create-react-app-cra" "source": "/getting-started/create-react-app/"
}, },
{ {
"source": "/getting-started/gatsby/", "destination": "/docs/getting-started/#gatsby",
"destination": "/docs/getting-started/#gatsby" "source": "/getting-started/gatsby/"
}, },
{ {
"source": "/getting-started/next/", "destination": "/docs/getting-started/#nextjs",
"destination": "/docs/getting-started/#nextjs" "source": "/getting-started/next/"
}, },
{ {
"source": "/getting-started/parcel/", "destination": "/docs/getting-started/#parcel",
"destination": "/docs/getting-started/#parcel" "source": "/getting-started/parcel/"
}, },
{ {
"source": "/getting-started/react-static/", "destination": "/docs/getting-started/#react-static",
"destination": "/docs/getting-started/#react-static" "source": "/getting-started/react-static/"
}, },
{ {
"source": "/getting-started/table-of-components/", "destination": "/table-of-components/",
"destination": "/table-of-components/" "source": "/getting-started/table-of-components/"
}, },
{ {
"source": "/getting-started/typescript/", "destination": "/docs/getting-started/#types",
"destination": "/docs/getting-started/#types" "source": "/getting-started/typescript/"
}, },
{ {
"source": "/getting-started/webpack/", "destination": "/docs/getting-started/#webpack",
"destination": "/docs/getting-started/#webpack" "source": "/getting-started/webpack/"
}, },
{ {
"source": "/getting-started/", "destination": "/docs/getting-started/",
"destination": "/docs/getting-started/" "source": "/getting-started/"
}, },
{ {
"source": "/guides/custom-loader/", "destination": "/guides/frontmatter/",
"destination": "/guides/frontmatter/" "source": "/guides/custom-loader/"
}, },
{ {
"source": "/guides/live-code/", "destination": "/guides/syntax-highlighting/#syntax-highlighting-with-the-meta-field",
"destination": "/guides/syntax-highlighting/#syntax-highlighting-with-the-meta-field" "source": "/guides/live-code/"
}, },
{ {
"source": "/guides/markdown-in-components/", "destination": "/docs/what-is-mdx/",
"destination": "/docs/what-is-mdx/" "source": "/guides/markdown-in-components/"
}, },
{ {
"source": "/guides/math-blocks/", "destination": "/guides/math/",
"destination": "/guides/math/" "source": "/guides/math-blocks/"
}, },
{ {
"source": "/guides/mdx-embed/", "destination": "/guides/embed/#embeds-at-run-time",
"destination": "/guides/embed/#embeds-at-run-time" "source": "/guides/mdx-embed/"
}, },
{ {
"source": "/guides/table-of-contents/", "destination": "/docs/extending-mdx/",
"destination": "/docs/extending-mdx/" "source": "/guides/table-of-contents/"
}, },
{ {
"source": "/guides/terminal/", "destination": "/docs/getting-started/#ink",
"destination": "/docs/getting-started/#ink" "source": "/guides/terminal/"
}, },
{ {
"source": "/guides/vue/", "destination": "/docs/getting-started/#vue",
"destination": "/docs/getting-started/#vue" "source": "/guides/vue/"
}, },
{ {
"source": "/guides/wrapper-customization/", "destination": "/docs/using-mdx/#layout",
"destination": "/docs/using-mdx/#layout" "source": "/guides/wrapper-customization/"
}, },
{ {
"source": "/guides/writing-a-plugin/", "destination": "/docs/extending-mdx/#creating-plugins",
"destination": "/docs/extending-mdx/#creating-plugins" "source": "/guides/writing-a-plugin/"
}, },
{ {
"source": "/mdx/", "destination": "/docs/what-is-mdx/",
"destination": "/docs/what-is-mdx/" "source": "/mdx/"
}, },
{ {
"source": "/plugins/", "destination": "/docs/extending-mdx/#using-plugins",
"destination": "/docs/extending-mdx/#using-plugins" "source": "/plugins/"
}, },
{ {
"source": "/projects/", "destination": "/community/projects/",
"destination": "/community/projects/" "source": "/projects/"
}, },
{ {
"source": "/support/", "destination": "/community/support/",
"destination": "/community/support/" "source": "/support/"
}, },
{ {
"source": "/syntax/", "destination": "/docs/getting-started/#syntax",
"destination": "/docs/getting-started/#syntax" "source": "/syntax/"
}, },
{ {
"source": "/vue/", "destination": "/docs/getting-started/#vue",
"destination": "/docs/getting-started/#vue" "source": "/vue/"
} }
] ]
} }

View File

@ -37,8 +37,10 @@
import assert from 'node:assert' import assert from 'node:assert'
import fs from 'node:fs/promises' import fs from 'node:fs/promises'
import {fileURLToPath} from 'node:url' import {fileURLToPath} from 'node:url'
import structuredClone from '@ungap/structured-clone'
import {globby} from 'globby' import {globby} from 'globby'
import {h} from 'hastscript' import {h} from 'hastscript'
import {sanitize} from 'hast-util-sanitize'
import {select} from 'hast-util-select' import {select} from 'hast-util-select'
import pAll from 'p-all' import pAll from 'p-all'
import React from 'react' import React from 'react'
@ -48,7 +50,6 @@ import rehypeMeta from 'rehype-meta'
import rehypeMinifyUrl from 'rehype-minify-url' import rehypeMinifyUrl from 'rehype-minify-url'
import rehypeParse from 'rehype-parse' import rehypeParse from 'rehype-parse'
import rehypePresetMinify from 'rehype-preset-minify' import rehypePresetMinify from 'rehype-preset-minify'
import rehypeSanitize from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify' import rehypeStringify from 'rehype-stringify'
import {unified} from 'unified' import {unified} from 'unified'
import {VFile} from 'vfile' import {VFile} from 'vfile'
@ -111,10 +112,10 @@ const allInfo = await pAll(
// Sanitize the hast description: // Sanitize the hast description:
if (data.meta && data.meta.descriptionHast) { if (data.meta && data.meta.descriptionHast) {
data.meta.descriptionHast = unified() // Cast because we get a root back.
.use(rehypeSanitize, schema) data.meta.descriptionHast = /** @type {Root} */ (
// @ts-expect-error: to do: use hast utils; element is fine. sanitize(data.meta.descriptionHast, schema)
.runSync(data.meta.descriptionHast) )
} }
return {Content, data, ghUrl, jsonUrl, name, url} return {Content, data, ghUrl, jsonUrl, name, url}
@ -197,8 +198,7 @@ await pAll(
.use(rehypeParse, {fragment: true}) .use(rehypeParse, {fragment: true})
.use(rehypeDocument, { .use(rehypeDocument, {
css: ['/index.css'], css: ['/index.css'],
// To do: only include editor on playground? // Idea: only include editor on playground? Use more editors.
// Or use more editors.
js: ['/index.js', '/editor.js'], js: ['/index.js', '/editor.js'],
language: 'en', language: 'en',
link: [ link: [
@ -254,7 +254,7 @@ await pAll(
]) ])
.use(rehypePresetMinify) .use(rehypePresetMinify)
.use(rehypeMinifyUrl, {from: canonical.href}) .use(rehypeMinifyUrl, {from: canonical.href})
.use(rehypeStringify, {bogusComments: false}) .use(rehypeStringify)
.process( .process(
new VFile({ new VFile({
data, data,
@ -296,17 +296,16 @@ function rehypeLazyCss(styles) {
let index = -1 let index = -1
while (++index < styles.length) { while (++index < styles.length) {
// To do: structured clone.
const props = styles[index] const props = styles[index]
enabled.push( enabled.push(
h('link', { h('link', {
...props, ...structuredClone(props),
as: 'style', as: 'style',
onLoad: "this.onload=undefined;this.rel='stylesheet'", onLoad: "this.onload=undefined;this.rel='stylesheet'",
rel: 'preload' rel: 'preload'
}) })
) )
disabled.push(h('link', {...props, rel: 'stylesheet'})) disabled.push(h('link', {...structuredClone(props), rel: 'stylesheet'}))
} }
head.children.push(...enabled) head.children.push(...enabled)

View File

@ -70,10 +70,8 @@ const options = {
} }
], ],
[rehypeStarryNight, {grammars: [...common, sourceMdx, sourceTsx]}], [rehypeStarryNight, {grammars: [...common, sourceMdx, sourceTsx]}],
// Minify things — mostly needed so `prerender` can also minify.
// To do: can be removed?
rehypePresetMinify, rehypePresetMinify,
[rehypeMinifyUrl, {ignoreMissingSource: true}] rehypeMinifyUrl
], ],
remarkPlugins: [ remarkPlugins: [
remarkFrontmatter, remarkFrontmatter,
@ -84,15 +82,7 @@ const options = {
[remarkMdxFrontmatter, {name: 'matter'}], [remarkMdxFrontmatter, {name: 'matter'}],
remarkStripBadges, remarkStripBadges,
remarkSqueezeParagraphs, remarkSqueezeParagraphs,
[ [remarkToc, {maxDepth: 3}]
remarkToc,
{
// To do: remove, this is the default now.
heading: 'contents|toc|table[ -]of[ -]contents?',
maxDepth: 3,
tight: true
}
]
] ]
} }

View File

@ -25,16 +25,13 @@ import process from 'node:process'
import {fileURLToPath} from 'node:url' import {fileURLToPath} from 'node:url'
import chromium from '@sparticuz/chromium' import chromium from '@sparticuz/chromium'
import {globby} from 'globby' import {globby} from 'globby'
import {fromHtml} from 'hast-util-from-html'
import {sanitize, defaultSchema} from 'hast-util-sanitize'
import {select} from 'hast-util-select' import {select} from 'hast-util-select'
import {toHtml} from 'hast-util-to-html'
import {h, s} from 'hastscript' import {h, s} from 'hastscript'
import pAll from 'p-all' import pAll from 'p-all'
import puppeteer from 'puppeteer' import puppeteer from 'puppeteer'
import rehypeParse from 'rehype-parse'
import rehypeSanitize, {defaultSchema} from 'rehype-sanitize'
import rehypeStringify from 'rehype-stringify'
import {unified} from 'unified'
import {u} from 'unist-builder'
import {VFile} from 'vfile'
import {rss} from 'xast-util-feed' import {rss} from 'xast-util-feed'
import {toXml} from 'xast-util-to-xml' import {toXml} from 'xast-util-to-xml'
import {config} from '../docs/_config.js' import {config} from '../docs/_config.js'
@ -102,46 +99,34 @@ const entries = await pAll(
*/ */
return async function () { return async function () {
const buf = await fs.readFile(new URL('index.html', url)) const buf = await fs.readFile(new URL('index.html', url))
const file = await unified() const tree = fromHtml(buf)
.use(rehypeParse) const body = select('.body', tree)
.use(function () { assert(body)
/** const clean = sanitize(body, {
* @param {Root} tree ...defaultSchema,
* Tree attributes: {
*/ ...defaultSchema.attributes,
return function (tree) { code: [
const node = select('.body', tree) [
assert(node) 'className',
return {type: 'root', children: node.children} 'language-diff',
} 'language-html',
}) 'language-js',
.use(rehypeSanitize, { 'language-jsx',
...defaultSchema, 'language-md',
attributes: { 'language-mdx',
...defaultSchema.attributes, 'language-sh',
code: [ 'language-ts'
[
'className',
'language-diff',
'language-html',
'language-js',
'language-jsx',
'language-md',
'language-mdx',
'language-sh',
'language-ts'
]
] ]
}, ]
clobber: [] },
}) clobber: []
.use(rehypeStringify) })
.process(buf)
return { return {
author: info.meta.author, author: info.meta.author,
description: info.meta.description, description: info.meta.description,
descriptionHtml: String(file), descriptionHtml: toHtml(clean),
modified: info.meta.modified, modified: info.meta.modified,
published: info.meta.published, published: info.meta.published,
title: info.meta.title, title: info.meta.title,
@ -205,14 +190,10 @@ await pAll(
// Dont regenerate to improve performance. // Dont regenerate to improve performance.
if (stats) return if (stats) return
const processor = unified().use(rehypeStringify) const value = toHtml({
const file = new VFile({path: url}) type: 'root',
children: [
// To do: use hast instead of unified? {type: 'doctype'},
file.value = processor.stringify(
u('root', [
// To do: remove `name`.
u('doctype', {name: 'html'}),
h('html', {lang: 'en'}, [ h('html', {lang: 'en'}, [
h('head', [ h('head', [
h('meta', {charSet: 'utf8'}), h('meta', {charSet: 'utf8'}),
@ -337,10 +318,7 @@ await pAll(
h('.og-description', [ h('.og-description', [
h('.og-description-inside', [ h('.og-description-inside', [
info.meta.descriptionHast info.meta.descriptionHast
? unified() ? sanitize(info.meta.descriptionHast, schema)
.use(rehypeSanitize, schema)
// @ts-expect-error: to do: use hast utilities; element is fine.
.runSync(info.meta.descriptionHast)
: info.meta.description || info.matter.description : info.meta.description || info.matter.description
]) ])
]), ]),
@ -364,8 +342,8 @@ await pAll(
]) ])
]) ])
]) ])
]) ]
) })
try { try {
await fs.unlink(output) await fs.unlink(output)
@ -377,7 +355,7 @@ await pAll(
await page.emulateMediaFeatures([ await page.emulateMediaFeatures([
{name: 'prefers-color-scheme', value: 'light'} {name: 'prefers-color-scheme', value: 'light'}
]) ])
await page.setContent(file.value) await page.setContent(value)
const screenshot = await page.screenshot() const screenshot = await page.screenshot()
await page.close() await page.close()

View File

@ -21,7 +21,6 @@ import rehypeMinifyUrl from 'rehype-minify-url'
import rehypePresetMinify from 'rehype-preset-minify' import rehypePresetMinify from 'rehype-preset-minify'
import rehypeStringify from 'rehype-stringify' import rehypeStringify from 'rehype-stringify'
import {unified} from 'unified' import {unified} from 'unified'
import {u} from 'unist-builder'
import {VFile} from 'vfile' import {VFile} from 'vfile'
import {config, redirect} from '../docs/_config.js' import {config, redirect} from '../docs/_config.js'
@ -105,29 +104,31 @@ console.log('✔ `vercel.json` redirects')
* *
* @param {string} to * @param {string} to
* Redirect to. * Redirect to.
* @returns {Root} * @returns {Root}
* Tree. * Tree.
*/ */
function buildRedirect(to) { function buildRedirect(to) {
const abs = new URL(to, config.site) const abs = new URL(to, config.site)
return u('root', [ return {
// To do: remove `name`. type: 'root',
u('doctype', {name: 'html'}), children: [
h('html', {lang: 'en'}, [ {type: 'doctype'},
h('head', [ h('html', {lang: 'en'}, [
h('meta', {charSet: 'utf8'}), h('head', [
h('title', 'Redirecting…'), h('meta', {charSet: 'utf8'}),
h('link', {href: String(abs), rel: 'canonical'}), h('title', 'Redirecting…'),
h('script', 'location = ' + JSON.stringify(abs)), h('link', {href: String(abs), rel: 'canonical'}),
h('meta', {content: '0;url=' + abs, httpEquiv: 'refresh'}), h('script', 'location = ' + JSON.stringify(abs)),
h('meta', {content: 'noindex', name: 'robots'}) h('meta', {content: '0;url=' + abs, httpEquiv: 'refresh'}),
]), h('meta', {content: 'noindex', name: 'robots'})
h('body', [ ]),
h('h1', 'Redirecting…'), h('body', [
h('p', [ h('h1', 'Redirecting…'),
h('a', {href: String(abs)}, 'Click here if you are not redirected.') h('p', [
h('a', {href: String(abs)}, 'Click here if you are not redirected.')
])
]) ])
]) ])
]) ]
]) }
} }

View File

@ -1,9 +1,9 @@
/** /**
* @typedef {import('rehype-sanitize').Options} Options * @typedef {import('hast-util-sanitize').Schema} Schema
*/ */
/** /**
* @type {Readonly<Options>} * @type {Readonly<Schema>}
*/ */
export const schema = { export const schema = {
ancestors: {}, ancestors: {},