Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace MDXTag with custom pragma #401

Merged
merged 13 commits into from
Feb 26, 2019
2 changes: 1 addition & 1 deletion examples/gatsby/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "gatsby",
"version": "0.0.1",
"name": "gatsby-example",
"scripts": {
"start": "gatsby develop",
"build": "gatsby build"
Expand Down
25 changes: 5 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,9 @@
"plugins": [
"./packages/remark-mdx",
"preset-wooorm",
[
"lint-no-heading-punctuation",
false
],
[
"lint-maximum-line-length",
false
],
[
"validate-links",
false
]
["lint-no-heading-punctuation", false],
["lint-maximum-line-length", false],
["validate-links", false]
]
},
"husky": {
Expand All @@ -70,14 +61,8 @@
}
},
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
],
"*.md": [
"remark -qf",
"git add"
]
"*.js": ["eslint --fix", "git add"],
"*.md": ["remark -qf", "git add"]
},
"x0": {
"title": "MDX"
Expand Down
4 changes: 2 additions & 2 deletions packages/loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ module.exports = async function(content) {
return callback(err)
}

const code = `
const code = `/* @jsx mdx */
import React from 'react'
import { MDXTag } from '@mdx-js/tag'
import mdx from '@mdx-js/mdx/create-element'
${result}
`

Expand Down
59 changes: 59 additions & 0 deletions packages/mdx/create-element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const React = require('react')
const {withMDXComponents} = require('@mdx-js/tag')

const TYPE_PROP_NAME = '__MDX_TYPE_PLEASE_DO_NOT_USE__'

const DEFAULTS = {
inlineCode: 'code',
wrapper: 'div'
}

const MDXCreateElementInner = ({
components = {},
__MDX_TYPE_PLEASE_DO_NOT_USE__,
parentName,
...etc
}) => {
const type = __MDX_TYPE_PLEASE_DO_NOT_USE__
const Component =
components[`${parentName}.${type}`] ||
components[type] ||
DEFAULTS[type] ||
type

return React.createElement(Component, etc)
}
MDXCreateElementInner.displayName = 'MDXCreateElementInner'

const MDXCreateElement = withMDXComponents(MDXCreateElementInner)
MDXCreateElement.displayName = 'MDXCreateElement'

module.exports = function(type, props) {
const args = arguments

if (typeof type === 'string') {
const argsLength = args.length

const createElementArgArray = new Array(argsLength)
createElementArgArray[0] = MDXCreateElement

const newProps = {}
for (let key in props) {
if (hasOwnProperty.call(props, key)) {
newProps[key] = props[key]
}
}

newProps[TYPE_PROP_NAME] = type

createElementArgArray[1] = newProps

for (let i = 2; i < argsLength; i++) {
createElementArgArray[i] = args[i]
}

return React.createElement.apply(null, createElementArgArray)
}

return React.createElement.apply(null, args)
}
6 changes: 4 additions & 2 deletions packages/mdx/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ function sync(mdx, options) {

const {contents} = compiler.processSync(fileOpts)

return contents
return `/* @jsx mdx */
${contents}`
}

async function compile(mdx, options = {}) {
Expand All @@ -128,7 +129,8 @@ async function compile(mdx, options = {}) {

const {contents} = await compiler.process(fileOpts)

return contents
return `/* @jsx mdx */
${contents}`
}

compile.sync = sync
Expand Down
23 changes: 10 additions & 13 deletions packages/mdx/mdx-hast-to-jsx.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,18 +118,15 @@ ${skipExport ? '' : 'export default'} class MDXContent extends React.Component {
}
render() {
const { components, ...props } = this.props
const Layout = this.layout

return <MDXTag
return <div
name="wrapper"
${
layout
? `Layout={this.layout} layoutProps={Object.assign({}, layoutProps, props)}`
: ''
}
components={components}>${jsxNodes
.map(childNode => toJSX(childNode, node))
.join('')}
</MDXTag>
components={components}>
${layout ? `<Layout {...layoutProps} {...props}>` : ''}
${jsxNodes.map(childNode => toJSX(childNode, node)).join('')}
${layout ? `</Layout>` : ''}
</div>
}
}
MDXContent.isMDXComponent = true`
Expand Down Expand Up @@ -159,9 +156,9 @@ MDXContent.isMDXComponent = true`
props = JSON.stringify(node.properties)
}

return `<MDXTag name="${node.tagName}" components={components}${
parentNode.tagName ? ` parentName="${parentNode.tagName}"` : ''
}${props ? ` props={${props}}` : ''}>${children}</MDXTag>`
return `<${node.tagName} ${
parentNode.tagName ? `parentName="${parentNode.tagName}"` : ''
}${props ? ` {...${props}}` : ''}>${children}</${node.tagName}>`
}

// Wraps text nodes inside template string, so that we don't run into escaping issues.
Expand Down
1 change: 1 addition & 0 deletions packages/mdx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"files": [
"index.js",
"util.js",
"create-element.js",
"md-ast-to-mdx-ast.js",
"mdx-ast-to-mdx-hast.js",
"mdx-hast-to-jsx.js"
Expand Down
99 changes: 49 additions & 50 deletions packages/mdx/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const prism = require('@mapbox/rehype-prism')
const math = require('remark-math')
const katex = require('rehype-katex')
const prettier = require('prettier')
const {MDXTag, MDXProvider} = require('@mdx-js/tag')
const {MDXProvider} = require('@mdx-js/tag')
const createElement = require('../create-element')
const React = require('react')
const {renderToStaticMarkup} = require('react-dom/server')

Expand Down Expand Up @@ -35,13 +36,12 @@ const transform = code =>
const renderWithReact = async mdxCode => {
const jsx = await mdx(mdxCode, {skipExport: true})
const code = transform(jsx)
const scope = {MDXTag}
const scope = {mdx: createElement}

const fn = new Function( // eslint-disable-line no-new-func
'React',
...Object.keys(scope),
`
${code}; return React.createElement(MDXContent)`
`${code}; return React.createElement(MDXContent)`
)

const element = fn(React, ...Object.values(scope))
Expand Down Expand Up @@ -90,20 +90,23 @@ it('Should compile sample blog post', async () => {
it('Should match sample blog post snapshot', async () => {
const result = await mdx(`# Hello World`)

expect(prettier.format(result, {parser: 'babylon'})).toMatchInlineSnapshot(`
"const layoutProps = {};
expect(prettier.format(result, {parser: 'babel'})).toMatchInlineSnapshot(`
"/* @jsx mdx */

const layoutProps = {};
export default class MDXContent extends React.Component {
constructor(props) {
super(props);
this.layout = null;
}
render() {
const { components, ...props } = this.props;
const Layout = this.layout;

return (
<MDXTag name=\\"wrapper\\" components={components}>
<MDXTag name=\\"h1\\" components={components}>{\`Hello World\`}</MDXTag>
</MDXTag>
<div name=\\"wrapper\\" components={components}>
<h1>{\`Hello World\`}</h1>
</div>
);
}
}
Expand Down Expand Up @@ -133,9 +136,7 @@ it('Should properly expose comments', async () => {
it('Should render HTML inside inlineCode correctly', async () => {
const result = await mdx('`<div>`')

expect(result).toContain(
'<MDXTag name="inlineCode" components={components} parentName="p">{`<div>`}</MDXTag>'
)
expect(result).toContain('<inlineCode parentName="p">{`<div>`}</inlineCode>')
})

it('Should preserve newlines in code blocks', async () => {
Expand All @@ -149,7 +150,7 @@ COPY start.sh /home/start.sh
{hastPlugins: [prism]}
)

expect(result).toContain('{`# Add main script`}</MDXTag>{`\n`}')
expect(result).toContain('{`# Add main script`}</span>{`\n`}')
})

it('Should preserve infostring in code blocks', async () => {
Expand All @@ -163,7 +164,7 @@ COPY start.sh /home/start.sh
)

expect(result).toContain(
`props={{"className":"language-dockerfile","metastring":"exec registry=something.com","exec":true,"registry":"something.com"}}`
`{...{"className":"language-dockerfile","metastring":"exec registry=something.com","exec":true,"registry":"something.com"}}`
)
})

Expand Down Expand Up @@ -286,9 +287,7 @@ it('Should not include export wrapper if skipExport is true', async () => {

it('Should recognize components as properties', async () => {
const result = await mdx('# Hello\n\n<MDX.Foo />')
expect(result).toContain(
'<MDXTag name="h1" components={components}>{`Hello`}</MDXTag>\n<MDX.Foo />'
)
expect(result).toContain('<h1 >{`Hello`}</h1>\n<MDX.Foo />')
})

it('Should contain static isMDXComponent() function', async () => {
Expand Down Expand Up @@ -343,13 +342,9 @@ test('Should parse and render footnotes', async () => {
'This is a paragraph with a [^footnote]\n\n[^footnote]: Here is the footnote'
)

expect(result).toContain(
'<MDXTag name="sup" components={components} parentName="p" props={{"id":"fnref-footnote"}}>'
)
expect(result).toContain('<sup parentName="p" {...{"id":"fnref-footnote"}}>')

expect(result).toContain(
'<MDXTag name="li" components={components} parentName="ol" props={{"id":"fn-footnote"}}>'
)
expect(result).toContain('<li parentName="ol" {...{"id":"fn-footnote"}}>')
}, 10000)

test('Should expose a sync compiler', () => {
Expand All @@ -362,7 +357,8 @@ test('Should handle layout props', () => {
const result = mdx.sync(fixtureBlogPost)

expect(result).toMatchInlineSnapshot(`
"import { Baz } from './Fixture'
"/* @jsx mdx */
import { Baz } from './Fixture'
import { Buz } from './Fixture'
export const foo = {
hi: \`Fudge \${Baz.displayName || 'Baz'}\`,
Expand All @@ -382,42 +378,45 @@ export default class MDXContent extends React.Component {
}
render() {
const { components, ...props } = this.props
const Layout = this.layout

return <MDXTag
return <div
name=\\"wrapper\\"
Layout={this.layout} layoutProps={Object.assign({}, layoutProps, props)}
components={components}>
<Layout {...layoutProps} {...props}>


<MDXTag name=\\"h1\\" components={components}>{\`Hello, world!\`}</MDXTag>
<MDXTag name=\\"p\\" components={components}>{\`I'm an awesome paragraph.\`}</MDXTag>
<h1 >{\`Hello, world!\`}</h1>
<p >{\`I'm an awesome paragraph.\`}</p>
{/* I'm a comment */}
<Foo bg='red'>
<Bar>hi</Bar>
{hello}
{/* another commment */}
</Foo>
<MDXTag name=\\"pre\\" components={components}><MDXTag name=\\"code\\" components={components} parentName=\\"pre\\" props={{}}>{\`test codeblock
\`}</MDXTag></MDXTag>
<MDXTag name=\\"pre\\" components={components}><MDXTag name=\\"code\\" components={components} parentName=\\"pre\\" props={{\\"className\\":\\"language-js\\"}}>{\`module.exports = 'test'
\`}</MDXTag></MDXTag>
<MDXTag name=\\"pre\\" components={components}><MDXTag name=\\"code\\" components={components} parentName=\\"pre\\" props={{\\"className\\":\\"language-sh\\"}}>{\`npm i -g foo
\`}</MDXTag></MDXTag>
<MDXTag name=\\"table\\" components={components}>
<MDXTag name=\\"thead\\" components={components} parentName=\\"table\\">
<MDXTag name=\\"tr\\" components={components} parentName=\\"thead\\">
<MDXTag name=\\"th\\" components={components} parentName=\\"tr\\" props={{\\"align\\":\\"left\\"}}>{\`Test\`}</MDXTag>
<MDXTag name=\\"th\\" components={components} parentName=\\"tr\\" props={{\\"align\\":\\"left\\"}}>{\`Table\`}</MDXTag>
</MDXTag>
</MDXTag>
<MDXTag name=\\"tbody\\" components={components} parentName=\\"table\\">
<MDXTag name=\\"tr\\" components={components} parentName=\\"tbody\\">
<MDXTag name=\\"td\\" components={components} parentName=\\"tr\\" props={{\\"align\\":\\"left\\"}}>{\`Col1\`}</MDXTag>
<MDXTag name=\\"td\\" components={components} parentName=\\"tr\\" props={{\\"align\\":\\"left\\"}}>{\`Col2\`}</MDXTag>
</MDXTag>
</MDXTag>
</MDXTag>

</MDXTag>
<pre ><code parentName=\\"pre\\" {...{}}>{\`test codeblock
\`}</code></pre>
<pre ><code parentName=\\"pre\\" {...{\\"className\\":\\"language-js\\"}}>{\`module.exports = 'test'
\`}</code></pre>
<pre ><code parentName=\\"pre\\" {...{\\"className\\":\\"language-sh\\"}}>{\`npm i -g foo
\`}</code></pre>
<table >
<thead parentName=\\"table\\">
<tr parentName=\\"thead\\">
<th parentName=\\"tr\\" {...{\\"align\\":\\"left\\"}}>{\`Test\`}</th>
<th parentName=\\"tr\\" {...{\\"align\\":\\"left\\"}}>{\`Table\`}</th>
</tr>
</thead>
<tbody parentName=\\"table\\">
<tr parentName=\\"tbody\\">
<td parentName=\\"tr\\" {...{\\"align\\":\\"left\\"}}>{\`Col1\`}</td>
<td parentName=\\"tr\\" {...{\\"align\\":\\"left\\"}}>{\`Col2\`}</td>
</tr>
</tbody>
</table>

</Layout>
</div>
}
}
MDXContent.isMDXComponent = true"
Expand Down
4 changes: 2 additions & 2 deletions packages/parcel-plugin-mdx/src/MDXAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class MDXAsset extends Asset {
{packageKey: 'mdx'}
)
const compiled = await mdx(this.contents, config)
const fullCode = `
const fullCode = `/* @jsx mdx */
import React from 'react';
import { MDXTag } from '@mdx-js/tag';
import mdx from '@mdx-js/mdx/create-element'
${compiled}
`
return [
Expand Down
Loading