-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Implement first pass of MDXs #507
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
const visit = require('unist-util-visit') | ||
const remove = require('unist-util-remove') | ||
const {toJSX} = require('@mdx-js/mdx/mdx-hast-to-jsx') | ||
|
||
module.exports = function({delimiter = 'hr'}) { | ||
this.Compiler = tree => { | ||
const splits = [] | ||
const documents = [] | ||
|
||
const importNodes = tree.children.filter(n => n.type === 'import') | ||
const exportNodes = tree.children.filter(n => n.type === 'export') | ||
|
||
const layout = exportNodes.find(node => node.default) | ||
|
||
// We don't care about imports and exports when handling | ||
// multiple MDX documents | ||
let mdxsTree = remove(remove(tree, 'export'), 'import') | ||
const {children} = mdxsTree | ||
|
||
visit(mdxsTree, node => { | ||
if (node.tagName === delimiter) { | ||
splits.push(children.indexOf(node)) | ||
} | ||
}) | ||
|
||
let previousSplit = 0 | ||
for (let i = 0; i < splits.length; i++) { | ||
const split = splits[i] | ||
documents.push(children.slice(previousSplit, split)) | ||
previousSplit = split + 1 | ||
} | ||
|
||
documents.push(children.slice(previousSplit)) | ||
|
||
const jsxFragments = documents | ||
.map(nodes => nodes.map(toJSX).join('\n')) | ||
.map((jsx, i) => | ||
` | ||
function MDXSContent${i}({ components, ...props }) { | ||
return ( | ||
${jsx.trim()} | ||
) | ||
} | ||
`.trim() | ||
) | ||
|
||
const defaultExport = ` | ||
const MDXSWrapper = props => [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we could actually make this the |
||
${jsxFragments.map((_, i) => ` <MDXSContent${i} {...props} />`).join(',\n')} | ||
] | ||
|
||
export default MDXWrapper | ||
`.trim() | ||
|
||
return [ | ||
importNodes.map(n => n.value.trim()).join('\n'), | ||
'', | ||
exportNodes | ||
.filter(n => !n.default) | ||
.map(n => n.value.trim()) | ||
.join('\n'), | ||
'', | ||
`const MDXSLayout = ${ | ||
layout | ||
? layout.value | ||
.replace(/^export\s+default\s+/, '') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should prolly make this a shared util. |
||
.replace(/;\s*$/, '') | ||
: '"wrapper"' | ||
}`, | ||
'', | ||
jsxFragments.join('\n\n'), | ||
'', | ||
defaultExport | ||
].join('\n') | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2019 John Otander and Brent Jackson | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
{ | ||
"name": "remark-mdxs", | ||
"version": "1.0.0-rc.0", | ||
"description": "Support for multiple MDX documents in a single file", | ||
"license": "MIT", | ||
"keywords": [ | ||
"mdx", | ||
"mdxs", | ||
"markdown", | ||
"react", | ||
"jsx", | ||
"remark", | ||
"mdxast" | ||
], | ||
"homepage": "https://mdxjs.com", | ||
"repository": "mdx-js/mdx", | ||
"bugs": "https://github.com/mdx-js/mdx/issues", | ||
"author": "John Otander <[email protected]> (https://johno.com)", | ||
"contributors": [ | ||
"John Otander <[email protected]> (http://johnotander.com)", | ||
"Brent Jackson <[email protected]> (https://jxnblk.com)", | ||
"Tim Neutkens <[email protected]>", | ||
"Matija Marohnić <[email protected]>", | ||
"Titus Wormer <[email protected]> (https://wooorm.com)" | ||
], | ||
"files": [ | ||
"index.js" | ||
], | ||
"dependencies": { | ||
"@babel/core": "^7.2.2", | ||
"@babel/helper-plugin-utils": "^7.0.0", | ||
"@babel/plugin-proposal-object-rest-spread": "^7.3.2", | ||
"@babel/plugin-syntax-jsx": "^7.2.0", | ||
"is-alphabetical": "^1.0.2", | ||
"remark-parse": "^6.0.0", | ||
"unified": "^7.0.0", | ||
"unist-util-remove": "^1.0.1", | ||
"unist-util-visit": "^1.4.0" | ||
}, | ||
"peerDependencies": { | ||
"@mdx-js/mdx": "*" | ||
}, | ||
"scripts": { | ||
"test": "jest" | ||
}, | ||
"jest": { | ||
"testEnvironment": "node" | ||
}, | ||
"devDependencies": { | ||
"jest": "^24.0.0", | ||
"remark-stringify": "^6.0.4", | ||
"vfile": "^4.0.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# @[mdx][]-js/mdxs | ||
|
||
[![Build Status][build-badge]][build] | ||
[![lerna][lerna-badge]][lerna] | ||
[![Join the community on Spectrum][spectrum-badge]][spectrum] | ||
|
||
> :warning: This project is currently in alpha | ||
|
||
Multi-doc syntax support for MDX. | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install --save mdxs | ||
``` | ||
|
||
## Usage | ||
|
||
```js | ||
const mdx = require('@mdx-js/mdx') | ||
const mdxs = require('@mdx-js/mdxs') | ||
|
||
const jsx = await mdx(FIXTURE, { | ||
compilers: [mdxs] | ||
}) | ||
``` | ||
|
||
## Syntax | ||
|
||
```md | ||
|
||
# Hello, I'm document 1 | ||
|
||
--- | ||
|
||
# Hello, I'm document 2 | ||
``` | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From a usage standpoint, is the thinking that you would use something like a separate webpack loader rule that only parses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that's the idea 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seems like it will turn out to be kind of unfortunate. It means we can't delineate between MDX content and MDXS content when fetching from remote sources that use Since every MDXs document is technically a valid MDX document, I'd much rather see a const export used to specify intent to be MDXs content. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, that's a good point 🤔. I'd mostly been thinking about local fs. How do you think intent for MDXs should be expressed? Something like?: export const MDXS = true There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah this is the approach I landed on as well. I think that the "first MDX file" in an MDXs document can be used as the "global exports" (compared to exports defined in subsequent "files"), so you'd end up with something like this where the first MDX doesn't have any content, but is just imports/exports.
|
||
## Contribute | ||
|
||
See [`contributing.md` in `mdx-js/mdx`][contributing] for ways to get started. | ||
|
||
This organisation has a [Code of Conduct][coc]. | ||
By interacting with this repository, organisation, or community you agree to | ||
abide by its terms. | ||
|
||
## License | ||
|
||
[MIT][] © [John Otander][author] | ||
|
||
<!-- Definitions --> | ||
|
||
[build]: https://travis-ci.org/mdx-js/mdx | ||
|
||
[build-badge]: https://travis-ci.org/mdx-js/mdx.svg?branch=master | ||
|
||
[lerna]: https://lernajs.io/ | ||
|
||
[lerna-badge]: https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg | ||
|
||
[spectrum]: https://spectrum.chat/mdx | ||
|
||
[spectrum-badge]: https://withspectrum.github.io/badge/badge.svg | ||
|
||
[contributing]: https://github.com/mdx-js/mdx/blob/master/contributing.md | ||
|
||
[coc]: https://github.com/mdx-js/mdx/blob/master/code-of-conduct.md | ||
|
||
[mit]: license | ||
|
||
[mdx]: https://github.com/mdx-js/mdx | ||
|
||
[author]: https://johno.com |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`correctly transpiles 1`] = ` | ||
"/* @jsx mdx */ | ||
import Foo from './bar' | ||
|
||
export const author = 'fred' | ||
|
||
const MDXSLayout = Foo | ||
|
||
function MDXSContent0({ components, ...props }) { | ||
return ( | ||
<h1>{\`Hello, world! \`}<Foo bar={{ baz: 'qux' }} /></h1> | ||
) | ||
} | ||
|
||
function MDXSContent1({ components, ...props }) { | ||
return ( | ||
<Baz> | ||
Hi! | ||
</Baz> | ||
) | ||
} | ||
|
||
function MDXSContent2({ components, ...props }) { | ||
return ( | ||
<h1>{\`I'm another document\`}</h1> | ||
|
||
|
||
<p>{\`over here.\`}</p> | ||
) | ||
} | ||
|
||
const MDXSWrapper = props => [ | ||
<MDXSContent0 {...props} />, | ||
<MDXSContent1 {...props} />, | ||
<MDXSContent2 {...props} /> | ||
] | ||
|
||
export default MDXWrapper" | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
const mdx = require('../../mdx') | ||
const remarkMdxs = require('..') | ||
|
||
const FIXTURE = ` | ||
import Foo from './bar' | ||
export const author = 'fred' | ||
export default Foo | ||
|
||
# Hello, world! <Foo bar={{ baz: 'qux' }} /> | ||
|
||
--- | ||
|
||
<Baz> | ||
Hi! | ||
</Baz> | ||
|
||
--- | ||
|
||
# I'm another document | ||
|
||
over here. | ||
` | ||
|
||
it('correctly transpiles', async () => { | ||
const result = await mdx(FIXTURE, { | ||
compilers: [remarkMdxs] | ||
}) | ||
|
||
expect(result).toMatchSnapshot() | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, I'm using
thematicBreak
in@mdx-deck/mdx-plugin
but with the way the delimiter is used below will eitherhr
orthematicBreak
work?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As is it won't work because we're operating on MDXHAST (since it's acting as a custom compiler for MDX). But it might make sense to make MDXS more standalone and not use
mdx
core andremark-mdx
directly instead. That would allow us to operate on the MDXAST.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding these architectural things, if
remark-mdxs
(or a different implementation of MDXs) were to be implemented today, do either of you (or @ChristianMurphy or @wooorm) have any recommendations for trying to make a new MDXs PR for MDX v2? Eg. either how / where the PR should be submitted, whether MDXs should be "more standalone" like described above, etc?@EricCote or @ProchaLu (or both) may take a shot at implementing something like this in the next weeks, see the link below. The first idea was to take the implementation in this PR and upgrade it to use MDX v2 APIs.