Skip to content

Commit

Permalink
Add support for named export default from
Browse files Browse the repository at this point in the history
Previously, only `export default const a = 1` was supported.
This adds support for `export {default} from "a"` and
`export {a as default} from "b"` declarations.

Closes GH-961.
Closes GH-1395.

Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
Reviewed-by: John Otander <johnotander@gmail.com>
  • Loading branch information
wooorm committed Dec 20, 2020
1 parent 9ce3619 commit 9d5d000
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 14 deletions.
68 changes: 54 additions & 14 deletions packages/mdx/mdx-hast-to-jsx.js
Expand Up @@ -16,15 +16,42 @@ function serializeEstree(estree, options) {

let layout
let children = []
let mdxLayoutDefault

// Find the `export default`, the JSX expression, and leave the rest
// (import/exports) as they are.
estree.body = estree.body.filter(child => {
// ```js
// export default a = 1
// ```
if (child.type === 'ExportDefaultDeclaration') {
layout = child.declaration
return false
}

// ```js
// export {default} from "a"
// export {default as a} from "b"
// export {default as a, b} from "c"
// export {a as default} from "b"
// export {a as default, b} from "c"
// ```
if (child.type === 'ExportNamedDeclaration' && child.source) {
// Remove `default` or `as default`, but not `default as`, specifier.
child.specifiers = child.specifiers.filter(specifier => {
if (specifier.exported.name === 'default') {
mdxLayoutDefault = {local: specifier.local, source: child.source}
return false
}

return true
})

// Keep the export if there are other specifiers, drop it if there was
// just a default.
return child.specifiers.length > 0
}

if (
child.type === 'ExpressionStatement' &&
(child.expression.type === 'JSXFragment' ||
Expand All @@ -42,7 +69,7 @@ function serializeEstree(estree, options) {

estree.body = [
...estree.body,
...createMdxLayout(layout),
...createMdxLayout(layout, mdxLayoutDefault),
...createMdxContent(children)
]

Expand Down Expand Up @@ -200,23 +227,36 @@ function createMdxContent(children) {
]
}

function createMdxLayout(declaration) {
function createMdxLayout(declaration, mdxLayoutDefault) {
const id = {type: 'Identifier', name: 'MDXLayout'}
const init = {type: 'Literal', value: 'wrapper', raw: '"wrapper"'}

return [
{
type: 'VariableDeclaration',
declarations: [
{
type: 'VariableDeclarator',
id: {type: 'Identifier', name: 'MDXLayout'},
init: declaration || {
mdxLayoutDefault
? {
type: 'ImportDeclaration',
specifiers: [
mdxLayoutDefault.local.name === 'default'
? {type: 'ImportDefaultSpecifier', local: id}
: {
type: 'ImportSpecifier',
imported: mdxLayoutDefault.local,
local: id
}
],
source: {
type: 'Literal',
value: 'wrapper',
raw: '"wrapper"'
value: mdxLayoutDefault.source.value,
raw: mdxLayoutDefault.source.raw
}
}
],
kind: 'const'
}
: {
type: 'VariableDeclaration',
declarations: [
{type: 'VariableDeclarator', id: id, init: declaration || init}
],
kind: 'const'
}
]
}

Expand Down
27 changes: 27 additions & 0 deletions packages/mdx/test/index.test.js
Expand Up @@ -476,6 +476,33 @@ describe('@mdx-js/mdx', () => {
)
})

it('should support a default export from an import', async () => {
let result = await mdx('import a from "b"\nexport default a')
expect(result).toMatch(/import a from "b"/)
expect(result).toMatch(/const MDXLayout = a/)

result = await mdx('export {default} from "a"')
expect(result).toMatch(/import MDXLayout from "a"/)

// These are not export defaults: they imports default but export as
// something else.
result = await mdx('export {default as a} from "b"')
expect(result).toMatch(/export { default as a } from "b"/)
expect(result).toMatch(/const MDXLayout = "wrapper"/)
result = await mdx('export {default as a, b} from "c"')
expect(result).toMatch(/export { default as a, b } from "c"/)
expect(result).toMatch(/const MDXLayout = "wrapper"/)

// These are export defaults.
result = await mdx('export {a as default} from "b"')
expect(result).toMatch(/import { a as MDXLayout } from "b"/)
expect(result).not.toMatch(/const MDXLayout/)
result = await mdx('export {a as default, b} from "c"')
expect(result).toMatch(/export { b } from "c"/)
expect(result).toMatch(/import { a as MDXLayout } from "c"/)
expect(result).not.toMatch(/const MDXLayout/)
})

it('should support semicolons in the default export', async () => {
const Content = await run(
'export default props => <section {...props} />;\n\nx'
Expand Down

0 comments on commit 9d5d000

Please sign in to comment.