1
1
mirror of https://github.com/mdx-js/mdx.git synced 2024-09-19 03:17:10 +03:00

Add support for React 18 (#1808)

Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
This commit is contained in:
Titus 2021-11-10 17:09:37 +01:00 committed by GitHub
parent 4afc80ca3c
commit 00b1db10d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 393 additions and 379 deletions

View File

@ -1,6 +1,6 @@
import React, {useState, useMemo, createElement, memo, useCallback} from 'react'
import {useDebounceFn} from 'ahooks'
import * as runtime from 'react/jsx-runtime.js'
import * as runtime from 'react/jsx-runtime'
import {VFile} from 'vfile'
import {VFileMessage} from 'vfile-message'
import {statistics} from 'vfile-statistics'

View File

@ -402,7 +402,7 @@ You can update your code as follows:
After:
```js path="new.js"
import * as runtime from 'react/jsx-runtime.js'
import * as runtime from 'react/jsx-runtime'
import * as provider from '@mdx-js/react'
import {evaluate} from '@mdx-js/mdx'
@ -431,7 +431,7 @@ Using Emotion as an example:
```js path="new-emotion.js"
// …
import * as runtime from '@emotion/react/jsx-runtime.js'
import * as runtime from '@emotion/react/jsx-runtime'
// …
```

614
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -172,6 +172,7 @@
],
"prettier": true,
"rules": {
"import/extensions": "off",
"node/file-extension-in-import": "off",
"react/prop-types": "off",
"unicorn/prefer-node-protocol": "off",

View File

@ -50,8 +50,8 @@
"@types/react-dom": "^17.0.0",
"@types/unist": "^2.0.0",
"esbuild": "^0.13.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react": "^18.0.0-alpha-327d5c484-20211106",
"react-dom": "^18.0.0-alpha-327d5c484-20211106",
"vfile-message": "^3.0.0"
},
"scripts": {

View File

@ -59,8 +59,8 @@
"babel-loader": "^8.0.0",
"preact": "^10.0.0",
"preact-render-to-string": "^5.0.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react": "^18.0.0-alpha-327d5c484-20211106",
"react-dom": "^18.0.0-alpha-327d5c484-20211106",
"vue": "^3.0.0",
"webpack": "^5.0.0"
},

View File

@ -64,8 +64,8 @@
"nanoid": "^3.0.0",
"preact": "^10.0.0",
"preact-render-to-string": "^5.0.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react": "^18.0.0-alpha-327d5c484-20211106",
"react-dom": "^18.0.0-alpha-327d5c484-20211106",
"rehype-katex": "^6.0.0",
"rehype-raw": "^6.0.0",
"remark-frontmatter": "^4.0.0",

View File

@ -688,7 +688,7 @@ They come from an automatic JSX runtime that you must import yourself.
<summary>Example</summary>
```js
import * as runtime from 'react/jsx-runtime.js'
import * as runtime from 'react/jsx-runtime'
const {default: Content} = await evaluate('# hi', {...runtime, ...otherOptions})
```
@ -704,7 +704,7 @@ Needed if you want to support a provider.
```js
import * as provider from '@mdx-js/react'
import * as runtime from 'react/jsx-runtime.js'
import * as runtime from 'react/jsx-runtime'
const {default: Content} = await evaluate('# hi', {...provider, ...runtime, ...otherOptions})
```
@ -723,7 +723,7 @@ that was exported from the MDX file available too.
Assuming the contents of `example.mdx` from [§ Use][use] was in `file`, then:
```js
import * as runtime from 'react/jsx-runtime.js'
import * as runtime from 'react/jsx-runtime'
import {evaluate} from '@mdx-js/mdx'
console.log(await evaluate(file, {...runtime}))

View File

@ -145,10 +145,10 @@ test('compile', async () => {
'should support the automatic runtime (`@jsxRuntime`)'
)
console.log(
'\nnote: the next deprecation is expected (preact is missing an export map)\n'
)
// console.log(
// '\nnote: the next deprecation is expected (preact is missing an export map)\n'
// )
//
// To do: re-enable when `preact/compat` has a correct export map.
// assert.equal(
// render(
@ -178,12 +178,7 @@ test('compile', async () => {
assert.equal(
render(
h(
await run(compileSync('<>1</>', {jsxImportSource: 'preact'}), {
keepImport: true
}),
{}
)
h(await run(compileSync('<>1</>', {jsxImportSource: 'preact'})), {})
),
'1',
'should support `jsxImportSource` for `preact`'
@ -198,8 +193,7 @@ test('compile', async () => {
).replace(
/\/jsx-runtime(?=["'])/g,
'$&/dist/emotion-react-jsx-runtime.cjs.prod.js'
),
{keepImport: true}
)
)
)
),
@ -947,7 +941,7 @@ test('markdown (GFM, with `remark-gfm`)', async () => {
await run(compileSync('* [x] a\n* [ ] b', {remarkPlugins: [remarkGfm]}))
)
),
'<ul class="contains-task-list">\n<li class="task-list-item"><input type="checkbox" checked="" disabled=""/> a</li>\n<li class="task-list-item"><input type="checkbox" disabled=""/> b</li>\n</ul>',
'<ul class="contains-task-list">\n<li class="task-list-item"><input type="checkbox" disabled="" checked=""/> a</li>\n<li class="task-list-item"><input type="checkbox" disabled=""/> b</li>\n</ul>',
'should support task lists (`* [x]` -> `input`)'
)
@ -1322,10 +1316,7 @@ test('source maps', async () => {
Buffer.from(JSON.stringify(file.map)).toString('base64') +
'\n'
await fs.writeFile(
path.join(base, 'sourcemap.js'),
String(file).replace(/\/jsx-runtime(?=["'])/g, '$&.js')
)
await fs.writeFile(path.join(base, 'sourcemap.js'), String(file))
const Content = /** @type {MDXContent} */ (
/* @ts-expect-error file is dynamically generated */
@ -1356,32 +1347,21 @@ test.run()
/**
*
* @param {VFileCompatible} input
* @param {{keepImport?: boolean}} [options]
* @return {Promise<MDXContent>}
*/
async function run(input, options = {}) {
return (await runWhole(input, options)).default
async function run(input) {
return (await runWhole(input)).default
}
/**
*
* @param {VFileCompatible} input
* @param {{keepImport?: boolean}} [options]
* @return {Promise<MDXModule>}
*/
async function runWhole(input, options = {}) {
async function runWhole(input) {
const name = 'fixture-' + nanoid().toLowerCase() + '.js'
const fp = path.join('test', 'context', name)
let doc = String(input)
// Extensionless imports only work in faux-ESM (webpack and such),
// *not* in Node by default: *except* if theres an export map defined
// in `package.json`.
// React doesnt have one yet (its on `master` but not yet released), so add
// the extension for em:
if (!options.keepImport) {
doc = doc.replace(/\/jsx-runtime(?=["'])/g, '$&.js')
}
const doc = String(input)
await fs.writeFile(fp, doc)

View File

@ -1,5 +1,14 @@
/**
* @typedef {import('@mdx-js/mdx/lib/compile.js').CompileOptions} CompileOptions
*
* @typedef LoaderOptions
* @property {boolean} [fixRuntimeWithoutExportMap=true]
* Several JSX runtimes, notably React and Emotion, dont yet have a proper
* export map set up.
* Export maps are needed to map `xxx/jsx-runtime` to an actual file in ESM.
* This option fixes React et al by turning those into `xxx/jsx-runtime.js`.
*
* @typedef {CompileOptions & LoaderOptions} Options
*/
import {promises as fs} from 'node:fs'
@ -11,10 +20,18 @@ import {createFormatAwareProcessors} from '@mdx-js/mdx/lib/util/create-format-aw
/**
* Create smart processors to handle different formats.
*
* @param {CompileOptions} [options]
* @param {Options} [options]
*/
export function createLoader(options) {
export function createLoader(options = {}) {
const {extnames, process} = createFormatAwareProcessors(options)
let fixRuntimeWithoutExportMap = options.fixRuntimeWithoutExportMap
if (
fixRuntimeWithoutExportMap === null ||
fixRuntimeWithoutExportMap === undefined
) {
fixRuntimeWithoutExportMap = true
}
return {load, getFormat, transformSource}
@ -30,16 +47,16 @@ export function createLoader(options) {
return defaultLoad(url, context, defaultLoad)
}
const fp = fileURLToPath(new URL(url))
const value = await fs.readFile(fp)
/* eslint-disable-next-line security/detect-non-literal-fs-filename */
const value = await fs.readFile(fileURLToPath(new URL(url)))
const file = await process(new VFile({value, path: new URL(url)}))
let source = String(file)
// V8 on Erbium.
/* c8 ignore next 2 */
return {
format: 'module',
source: String(file).replace(/\/jsx-runtime(?=["'])/g, '$&.js')
if (fixRuntimeWithoutExportMap) {
source = String(file).replace(/\/jsx-runtime(?=["'])/g, '$&.js')
}
return {format: 'module', source}
}
// Pre version 17.
@ -69,9 +86,13 @@ export function createLoader(options) {
}
const file = await process(new VFile({value, path: new URL(context.url)}))
// V8 on Erbium.
/* c8 ignore next 2 */
return {source: String(file).replace(/\/jsx-runtime(?=["'])/g, '$&.js')}
let source = String(file)
if (fixRuntimeWithoutExportMap) {
source = String(file).replace(/\/jsx-runtime(?=["'])/g, '$&.js')
}
return {source}
}
/* c8 ignore end */
}

View File

@ -43,13 +43,13 @@
"devDependencies": {
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"react": "^17.0.0",
"react-dom": "^17.0.0"
"react": "^18.0.0-alpha-327d5c484-20211106",
"react-dom": "^18.0.0-alpha-327d5c484-20211106"
},
"scripts": {
"prepack": "npm run build",
"build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage",
"test-api": "node --no-warnings --experimental-loader=./index.js ../../node_modules/uvu/bin.js test \"\\.js$\"",
"test-api": "node --no-warnings --experimental-loader=./test/react-18-node-loader.js ../../node_modules/uvu/bin.js test \"\\.js$\"",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api",
"test": "npm run build && npm run test-coverage"
},

View File

@ -0,0 +1,8 @@
import {createLoader} from '../index.js'
// Load is for Node 17+, the rest for 12, 14, 16.
const {load, getFormat, transformSource} = createLoader({
fixRuntimeWithoutExportMap: false
})
export {load, getFormat, transformSource}

View File

@ -51,8 +51,8 @@
"@mdx-js/mdx": "^2.0.0-rc.1",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"react": "^17.0.0",
"react-dom": "^17.0.0"
"react": "^18.0.0-alpha-327d5c484-20211106",
"react-dom": "^18.0.0-alpha-327d5c484-20211106"
},
"scripts": {
"prepack": "npm run build",

View File

@ -2,7 +2,7 @@ import {test} from 'uvu'
import * as assert from 'uvu/assert'
import {evaluate} from '@mdx-js/mdx'
import React from 'react'
import * as runtime from 'react/jsx-runtime.js'
import * as runtime from 'react/jsx-runtime'
import {renderToString} from 'react-dom/server.js'
import {MDXProvider, useMDXComponents, withMDXComponents} from '../index.js'

View File

@ -43,8 +43,8 @@
"devDependencies": {
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"react": "^17.0.0",
"react-dom": "^17.0.0"
"react": "^18.0.0-alpha-327d5c484-20211106",
"react-dom": "^18.0.0-alpha-327d5c484-20211106"
},
"scripts": {
"#prepack": "npm run build",

View File

@ -48,8 +48,8 @@
"devDependencies": {
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react": "^18.0.0-alpha-327d5c484-20211106",
"react-dom": "^18.0.0-alpha-327d5c484-20211106",
"rollup": "^2.0.0"
},
"scripts": {

View File

@ -25,10 +25,7 @@ test('@mdx-js/rollup', async () => {
const output = (await bundle.generate({format: 'es', sourcemap: true})).output
await fs.writeFile(
new URL('./rollup.js', import.meta.url),
output[0].code.replace(/\/jsx-runtime(?=["'])/g, '$&.js')
)
await fs.writeFile(new URL('./rollup.js', import.meta.url), output[0].code)
const Content = /** @type {MDXContent} */ (
/* @ts-expect-error file is dynamically generated */

View File

@ -1,5 +1,5 @@
import {promises as fs} from 'node:fs'
import path from 'path'
import {promises as fs} from 'node:fs'
import {URL, fileURLToPath} from 'node:url'
import {transformSync} from 'esbuild'

View File

@ -6,6 +6,8 @@
* @typedef {Root|Content} Node
*/
import path from 'path'
import process from 'process'
import {pathToFileURL} from 'url'
import remarkFrontmatter from 'remark-frontmatter'
import remarkGfm from 'remark-gfm'
@ -35,6 +37,10 @@ import {config} from '../docs/_config.js'
const own = {}.hasOwnProperty
const reactUrl = pathToFileURL(
path.resolve(process.cwd(), 'node_modules', 'react')
)
const options = {
remarkPlugins: [
remarkGfm,
@ -84,7 +90,8 @@ const options = {
rehypePresetMinify,
[rehypeMinifyUrl, {ignoreMissingSource: true}]
],
recmaPlugins: [recmaInjectMeta]
recmaPlugins: [recmaInjectMeta],
jsxImportSource: reactUrl.href
}
export default options