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

Update @mdx-js/preact (#1660)

* Use ESM
* Add JSDoc based types (and improve the types a bunch)
* Mark `MDXContext` and `withMDXComponents` exports as deprecated
  (they still work)
* Remove `mdx` export, which is no longer needed
* Use `xdm` for a second
This commit is contained in:
Titus 2021-09-16 11:45:23 +02:00 committed by GitHub
parent 897a6b23a7
commit 0262e20259
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 208 additions and 417 deletions

3
.gitignore vendored
View File

@ -18,3 +18,6 @@ coverage/
/packages/react/lib/**/*.d.ts
/packages/react/test/**/*.d.ts
/packages/react/index.d.ts
/packages/preact/lib/**/*.d.ts
/packages/preact/test/**/*.d.ts
/packages/preact/index.d.ts

18
package-lock.json generated
View File

@ -125,6 +125,7 @@
}
},
"examples/gatsby": {
"name": "gatsby-example",
"dependencies": {
"@mdx-js/mdx": "^1.0.0",
"@mdx-js/react": "^1.0.0",
@ -397,6 +398,7 @@
}
},
"examples/next": {
"name": "next-example",
"dependencies": {
"@mdx-js/loader": "^1.0.0",
"@mdx-js/mdx": "^1.0.0",
@ -686,6 +688,7 @@
}
},
"examples/parcel": {
"name": "parcel-example",
"devDependencies": {
"@mdx-js/react": "^1.0.0",
"@parcel/transformer-mdx": "^2.0.0-rc.0",
@ -735,6 +738,7 @@
}
},
"examples/react-web-components": {
"name": "react-web-components-example",
"dependencies": {
"@mdx-js/loader": "^1.0.0",
"@mdx-js/mdx": "^1.0.0",
@ -1022,6 +1026,7 @@
}
},
"examples/terminal": {
"name": "terminal-example",
"license": "MIT",
"dependencies": {
"@mdx-js/mdx": "^1.0.0",
@ -1301,6 +1306,7 @@
}
},
"examples/webpack": {
"name": "webpack-example",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.0.0",
@ -41685,6 +41691,7 @@
}
},
"packages/loader": {
"name": "@mdx-js/loader",
"version": "2.0.0-next.9",
"license": "MIT",
"dependencies": {
@ -41788,6 +41795,7 @@
}
},
"packages/mdx": {
"name": "@mdx-js/mdx",
"version": "2.0.0-next.9",
"license": "MIT",
"dependencies": {
@ -42161,11 +42169,13 @@
}
},
"packages/preact": {
"name": "@mdx-js/preact",
"version": "2.0.0-next.9",
"license": "MIT",
"devDependencies": {
"preact": "^10.0.0",
"preact-render-to-string": "^5.0.0"
"preact-render-to-string": "^5.0.0",
"xdm": "^2.0.0"
},
"funding": {
"type": "opencollective",
@ -42176,6 +42186,7 @@
}
},
"packages/react": {
"name": "@mdx-js/react",
"version": "2.0.0-next.999",
"license": "MIT",
"dependencies": {
@ -42304,6 +42315,7 @@
}
},
"packages/runtime": {
"name": "@mdx-js/runtime",
"version": "2.0.0-next.9",
"license": "MIT",
"dependencies": {
@ -42369,6 +42381,7 @@
}
},
"packages/vue": {
"name": "@mdx-js/vue",
"version": "2.0.0-next.9",
"license": "MIT",
"devDependencies": {
@ -44874,7 +44887,8 @@
"version": "file:packages/preact",
"requires": {
"preact": "^10.0.0",
"preact-render-to-string": "^5.0.0"
"preact-render-to-string": "^5.0.0",
"xdm": "^2.0.0"
}
},
"@mdx-js/react": {

View File

@ -44,11 +44,11 @@
"lint": "eslint --ext .jsx --report-unused-disable-directives --cache .",
"publish-ci": "# lerna publish -y --canary --preid ci --pre-dist-tag ci",
"publish-next": "# lerna publish --force-publish=\"*\" --pre-dist-tag next --preid next",
"build": "npm run build --workspaces -w packages/loader -w packages/react --if-present",
"build": "npm run build --workspaces -w packages/loader -w packages/react -w packages/preact --if-present",
"test-api": "npm run test-api --workspaces --if-present",
"test-coverage": "npm run test-coverage --workspaces --if-present",
"test-types": "npm run test-types --workspaces --if-present",
"test": "npm run lint && npm run test-coverage && npm run test-types"
"test": "npm run build && npm run lint && npm run test-coverage && npm run test-types"
},
"devDependencies": {
"@babel/core": "^7.0.0",

View File

@ -1,12 +0,0 @@
{
"presets": [
[
"@babel/env",
{
"corejs": 3,
"useBuiltIns": "usage"
}
],
"@babel/react"
]
}

20
packages/preact/complex-types.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
/**
* These types are in an actual TypeScript file because otherwise TS would
* expand them which will result in a giant file.
*/
import type {ComponentType} from 'preact'
type IntrinsicNames = keyof JSX.IntrinsicElements
type IntrinsicComponents = Partial<{
[TagName in IntrinsicNames]:
| IntrinsicNames
| ComponentType<JSX.IntrinsicElements[TagName]>
}>
type ExtrinsicComponents = {
[componentName: string]: ComponentType<any> | ExtrinsicComponents
}
export type Components = IntrinsicComponents & ExtrinsicComponents

10
packages/preact/index.js Normal file
View File

@ -0,0 +1,10 @@
/**
* @typedef {import('./complex-types').Components} Components
*/
export {
MDXContext,
MDXProvider,
useMDXComponents,
withMDXComponents
} from './lib/index.js'

View File

@ -0,0 +1,93 @@
/**
* @typedef {import('preact').ComponentChildren} ComponentChildren
* @typedef {import('../complex-types').Components} Components
*
* @typedef Props
* Configuration.
* @property {Components} [components]
* Mapping of names for JSX components to Preact components.
* @property {boolean} [disableParentContext=false]
* Turn off outer component context.
* @property {ComponentChildren} [children]
* Children.
*
* @callback MergeComponents
* @param {Components} currentComponents
* Current components from the context.
* @returns {Components}
* Merged components.
*/
import {createContext, h} from 'preact'
import {useContext} from 'preact/hooks'
/**
* @type {import('preact').Context<Components>}
* @deprecated
* This export is marked as a legacy feature.
* That means its no longer recommended for use as it might be removed
* in a future major release.
*
* Please use `useMDXComponents` to get context based components and
* `MDXProvider` to set context based components instead.
*/
export const MDXContext = createContext({})
/**
* @param {import('react').ComponentType<any>} Component
* @deprecated
* This export is marked as a legacy feature.
* That means its no longer recommended for use as it might be removed
* in a future major release.
*
* Please use `useMDXComponents` to get context based components instead.
*/
export function withMDXComponents(Component) {
return boundMDXComponent
/**
* @param {Record<string, unknown> & {components?: Components}} props
* @returns {JSX.Element}
*/
function boundMDXComponent(props) {
const allComponents = useMDXComponents(props.components)
return h(Component, {...props, allComponents})
}
}
/**
* Get current components from the MDX Context.
*
* @param {Components|MergeComponents} [components]
* Additional components to use or a function that takes the current
* components and filters/merges/changes them.
* @returns {Components}
* Current components.
*/
export function useMDXComponents(components) {
const contextComponents = useContext(MDXContext)
// Custom merge via a function prop
if (typeof components === 'function') {
return components(contextComponents)
}
return {...contextComponents, ...components}
}
/**
* Provider for MDX context
*
* @param {Props} props
* @returns {JSX.Element}
*/
export function MDXProvider({components, children, disableParentContext}) {
let allComponents = useMDXComponents(components)
if (disableParentContext) {
allComponents = components || {}
}
// @ts-expect-error: preact types are wrong.
return h(MDXContext.Provider, {value: allComponents}, children)
}

View File

@ -1,13 +1,22 @@
{
"name": "@mdx-js/preact",
"version": "2.0.0-next.9",
"description": "Preact implementation for MDX",
"description": "Preact context for MDX",
"license": "MIT",
"keywords": [
"mdx",
"markdown",
"preact",
"jsx",
"remark",
"mdxast"
],
"homepage": "https://mdxjs.com",
"repository": {
"type": "git",
"url": "https://github.com/mdx-js/mdx",
"directory": "packages/preact"
},
"homepage": "https://mdxjs.com",
"bugs": "https://github.com/mdx-js/mdx/issues",
"funding": {
"type": "opencollective",
@ -23,33 +32,37 @@
"Chris Biscardi <chris@christopherbiscardi.com> (https://www.christopherbiscardi.com)",
"Christian Murphy <christian.murphy.42@gmail.com>"
],
"license": "MIT",
"main": "src/index.js",
"types": "types/index.d.ts",
"type": "module",
"sideEffects": false,
"main": "index.js",
"types": "index.d.ts",
"files": [
"src",
"types/index.d.ts"
"lib/",
"complex-types.d.ts",
"index.d.ts",
"index.js"
],
"keywords": [
"mdx",
"markdown",
"react",
"jsx",
"remark",
"mdxast"
],
"scripts": {
"test-api": "uvu -r esbuild-register test \"\\.jsx?$\"",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api",
"test-types": "dtslint types",
"test": "npm run test-coverage && npm run test-types"
},
"dependencies": {},
"peerDependencies": {
"preact": ">=10.0.0"
},
"devDependencies": {
"preact": "^10.0.0",
"preact-render-to-string": "^5.0.0"
"preact-render-to-string": "^5.0.0",
"xdm": "^2.0.0"
},
"scripts": {
"prepack": "npm run build",
"build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"index.d.ts\" && tsc && type-coverage",
"test-api": "node --no-warnings --experimental-loader=../../script/jsx-loader.js ../../node_modules/uvu/bin.js test \"\\.jsx?$\"",
"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"
},
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true
},
"gitHead": "bf7deab69996449cb99c2217dff75e65855eb2c1"
}

View File

@ -1,37 +0,0 @@
const {createContext, h} = require('preact')
const {useContext} = require('preact/hooks')
const isFunction = obj => typeof obj === 'function'
const MDXContext = createContext({})
const withMDXComponents = Component => props => {
const allComponents = useMDXComponents(props.components)
return h(Component, {...props, allComponents})
}
const useMDXComponents = components => {
const contextComponents = useContext(MDXContext)
// Custom merge via a function prop
if (isFunction(components)) {
return components(contextComponents)
}
return {...contextComponents, ...components}
}
const MDXProvider = ({components, children, disableParentContext}) => {
let allComponents = useMDXComponents(components)
if (disableParentContext) {
allComponents = components
}
return h(MDXContext.Provider, {value: allComponents}, children)
}
exports.MDXContext = MDXContext
exports.MDXProvider = MDXProvider
exports.withMDXComponents = withMDXComponents
exports.useMDXComponents = useMDXComponents

View File

@ -1,81 +0,0 @@
const {h, Fragment} = require('preact')
const {forwardRef} = require('preact/compat')
import {useMDXComponents} from './context'
const TYPE_PROP_NAME = 'mdxType'
const DEFAULTS = {
inlineCode: 'code',
wrapper: ({children}) => h(Fragment, {}, children)
}
const MDXCreateElement = forwardRef((props, ref) => {
const {
components: propComponents,
mdxType,
originalType,
parentName,
...etc
} = props
const components = useMDXComponents(propComponents)
const type = mdxType
const Component =
components[`${parentName}.${type}`] ||
components[type] ||
DEFAULTS[type] ||
originalType
// To do: what is this useful for?
/* c8 ignore next 7 */
if (propComponents) {
return h(Component, {
ref,
...etc,
components: propComponents
})
}
return h(Component, {ref, ...etc})
})
MDXCreateElement.displayName = 'MDXCreateElement'
function mdx(type, props, ...rest) {
const args = [type, props, ...rest]
const mdxType = props && props.mdxType
if (typeof type === 'string' || mdxType) {
const argsLength = args.length
const createElementArgArray = new Array(argsLength)
createElementArgArray[0] = MDXCreateElement
const newProps = {}
for (const key in props) {
/* istanbul ignore else - folks putting stuff in `prototype`. */
if (hasOwnProperty.call(props, key)) {
newProps[key] = props[key]
}
}
newProps.originalType = type
newProps[TYPE_PROP_NAME] = typeof type === 'string' ? type : mdxType
createElementArgArray[1] = newProps
for (let i = 2; i < argsLength; i++) {
createElementArgArray[i] = args[i]
}
return h(...createElementArgArray)
}
return h(...args)
}
mdx.Fragment = Fragment
exports.mdx = mdx
exports.Fragment = Fragment

View File

@ -1,14 +0,0 @@
const {
MDXContext,
MDXProvider,
useMDXComponents,
withMDXComponents
} = require('./context')
const {mdx} = require('./create-element')
exports.MDXContext = MDXContext
exports.MDXProvider = MDXProvider
exports.useMDXComponents = useMDXComponents
exports.withMDXComponents = withMDXComponents
exports.mdx = mdx

View File

@ -1,95 +1,15 @@
/* @jsx h */
/* @jsxFrag Fragment */
import {test} from 'uvu'
import assert from 'uvu/assert'
import path from 'path'
import * as assert from 'uvu/assert'
import {h, Fragment} from 'preact'
import * as runtime from 'preact/jsx-runtime'
import {render} from 'preact-render-to-string'
import {transformAsync as babelTransform} from '@babel/core'
import mdxTransform from '../../mdx'
import {MDXProvider, withMDXComponents, mdx} from '../src'
const run = async value => {
// Turn the serialized MDX code into serialized JSX
const doc = await mdxTransform(value, {skipExport: true})
// and that into serialized JS.
const {code} = await babelTransform(doc, {
configFile: false,
plugins: [
'@babel/plugin-transform-react-jsx',
path.resolve(__dirname, '../../babel-plugin-remove-export-keywords')
]
})
// and finally run it, returning the component.
// eslint-disable-next-line no-new-func
return new Function('mdx', `${code}; return MDXContent`)(mdx)
}
test('should evaluate MDX code', async () => {
const Content = await run('# hi')
assert.equal(render(<Content />), '<h1>hi</h1>')
})
test('should evaluate some more complex MDX code (text, inline)', async () => {
const Content = await run(
'*a* **b** `c` <abbr title="Markdown + JSX">MDX</abbr>'
)
assert.equal(
render(<Content />),
'<p><em>a</em> <strong>b</strong> <code>c</code> <abbr title="Markdown + JSX">MDX</abbr></p>'
)
})
test('should evaluate some more complex MDX code (flow, block)', async () => {
const Content = await run('***\n> * 1. a')
assert.equal(
render(<Content />),
'<hr /><blockquote><ul><li><ol><li>a</li></ol></li></ul></blockquote>'
)
})
test('should warn on missing components', async () => {
const Content = await run('<Component />')
const calls = []
const {warn} = console
console.warn = (...parameters) => {
calls.push(parameters)
}
assert.equal(render(<Content />), '')
assert.equal(calls, [
[
'Component `%s` was not imported, exported, or provided by MDXProvider as global scope',
'Component'
]
])
console.warn = warn
})
test('should support components defined in MDX', async () => {
const Content = await run('export const A = () => <b>!</b>\n\n<A />')
assert.equal(render(<Content />), '<b>!</b>')
})
test('should not crash if weird values could come from JSX', async () => {
// As JSX is function calls, that function can also be used directly in
// MDX. Definitely not a great idea, but its an easy way to pass in funky
// values.
const Content = await run('{mdx(1)}')
assert.equal(render(<Content />), '<1></1>')
})
import {evaluate} from 'xdm'
import {MDXProvider, useMDXComponents, withMDXComponents} from '../index.js'
test('should support `components` with `MDXProvider`', async () => {
const Content = await run('# hi')
const {default: Content} = await evaluate('# hi', {...runtime, useMDXComponents})
assert.equal(
render(
@ -106,7 +26,7 @@ test('should support `components` with `MDXProvider`', async () => {
})
test('should support `wrapper` in `components`', async () => {
const Content = await run('# hi')
const {default: Content} = await evaluate('# hi', {...runtime, useMDXComponents})
assert.equal(
render(
@ -122,27 +42,8 @@ test('should support `wrapper` in `components`', async () => {
)
})
test('should support dots in component names (such as `ol.li`) for a direct child “selector”', async () => {
const Content = await run('* a\n1. b')
const OlLi = props => <li className="ordered" {...props} />
assert.equal(
render(
<MDXProvider
components={{
'ol.li': OlLi
}}
>
<Content />
</MDXProvider>
),
'<ul><li>a</li></ul><ol><li class="ordered">b</li></ol>'
)
})
test('should combine components in nested `MDXProvider`s', async () => {
const Content = await run('# hi\n## hello')
const {default: Content} = await evaluate('# hi\n## hello', {...runtime, useMDXComponents})
assert.equal(
render(
@ -161,12 +62,12 @@ test('should combine components in nested `MDXProvider`s', async () => {
</MDXProvider>
</MDXProvider>
),
'<h1 style="color: tomato;">hi</h1><h2 style="color: papayawhip;">hello</h2>'
'<h1 style="color: tomato;">hi</h1>\n<h2 style="color: papayawhip;">hello</h2>'
)
})
test('should support components as a function', async () => {
const Content = await run('# hi\n## hello')
const {default: Content} = await evaluate('# hi\n## hello', {...runtime, useMDXComponents})
assert.equal(
render(
@ -185,12 +86,12 @@ test('should support components as a function', async () => {
</MDXProvider>
</MDXProvider>
),
'<h1>hi</h1><h2 style="color: papayawhip;">hello</h2>'
'<h1>hi</h1>\n<h2 style="color: papayawhip;">hello</h2>'
)
})
test('should support a `disableParentContext` prop (sandbox)', async () => {
const Content = await run('# hi')
const {default: Content} = await evaluate('# hi', {...runtime, useMDXComponents})
assert.equal(
render(
@ -209,7 +110,7 @@ test('should support a `disableParentContext` prop (sandbox)', async () => {
})
test('should support `withComponents`', async () => {
const Content = await run('# hi\n## hello')
const {default: Content} = await evaluate('# hi\n## hello', {...runtime, useMDXComponents})
const With = withMDXComponents(props => <>{props.children}</>)
// To do: should this use the `h2` component too?
@ -229,7 +130,7 @@ test('should support `withComponents`', async () => {
</With>
</MDXProvider>
),
'<h1 style="color: tomato;">hi</h1><h2>hello</h2>'
'<h1 style="color: tomato;">hi</h1>\n<h2>hello</h2>'
)
})

View File

@ -0,0 +1,16 @@
{
"include": ["lib/**/*.js", "test/**/*.js", "index.js"],
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020"],
"module": "ES2020",
"moduleResolution": "node",
"allowJs": true,
"checkJs": true,
"declaration": true,
"emitDeclarationOnly": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"strict": true
}
}

View File

@ -1,74 +0,0 @@
// Minimum TypeScript Version: 3.8
import {h, Fragment} from 'preact'
// tslint:disable-next-line: no-duplicate-imports
import type {Context, AnyComponent, FunctionComponent, VNode} from 'preact'
/**
* Mapping of names for JSX components to React components
*/
interface ComponentDictionary {
[name: string]: AnyComponent<any>
}
/**
* Prop type that includes a component dictionary
*/
interface ComponentsProp {
/**
* Mapping of names for JSX components to React components
*/
components?: ComponentDictionary
/**
* Turn off outer component context
*
* @defaultValue false
*/
disableParentContext?: boolean
}
/**
* Direct access to the MDX React Context
*/
declare const MDXContext: Context<ComponentsProp>
/**
* Provider for MDX context
*/
declare const MDXProvider: FunctionComponent<ComponentsProp>
/**
* Gets components from the MDX Context
*
* @param components additional components to include
* @returns all components from context with overrides from components parameter
*/
declare function useMDXComponents(
components: ComponentDictionary | (() => ComponentDictionary)
): ComponentDictionary
/**
* High order component that passes components prop to child
*
* @param child Component being wrapped
*/
declare function withMDXComponents(
child: AnyComponent<ComponentsProp>
): VNode | null
/**
* Preact hyperscript function wrapped with handler for MDX content
*/
declare const mdx: typeof h & {
Fragment: typeof Fragment
}
export {
ComponentDictionary,
ComponentsProp,
MDXContext,
MDXProvider,
useMDXComponents,
withMDXComponents,
mdx
}

View File

@ -1,40 +0,0 @@
import {h, Fragment} from 'preact'
// tslint:disable-next-line: no-duplicate-imports
import type {ComponentChildren} from 'preact'
import {useContext} from 'preact/hooks'
import {
MDXProvider,
useMDXComponents,
withMDXComponents,
ComponentsProp,
MDXContext,
mdx
} from '@mdx-js/preact'
const H1 = ({children}: {children: ComponentChildren}) => <h1>{children}</h1>
const MDXProvideExample = () => (
<MDXProvider components={{h1: H1}} disableParentContext={true}>
<h1>Hello, world!</h1>
</MDXProvider>
)
const WithMDXComponentsExample = () =>
withMDXComponents(({components}: ComponentsProp) => {
components // $ExpectType ComponentDictionary | undefined
return <div />
})
const UseMDXComponentsExample = () => {
useMDXComponents({h1: H1}) // $ExpectType ComponentDictionary
useMDXComponents(() => ({h1: H1})) // $ExpectType ComponentDictionary
}
const UseMDXContextExample = () => {
const {components} = useContext(MDXContext)
components // $ExpectType ComponentDictionary | undefined
}
const MDXCreateElementExample = () => mdx('mdx', {title: 'example'}, [])
mdx.Fragment === Fragment

View File

@ -1,14 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["dom", "es6"],
"strict": true,
"baseUrl": ".",
"jsx": "react",
"jsxFactory": "h",
"paths": {
"@mdx-js/preact": ["index.d.ts"]
},
"types": []
}
}

View File

@ -1,7 +0,0 @@
{
"extends": "dtslint/dtslint.json",
"rules": {
"whitespace": false,
"semicolon": false
}
}