From 5846bf9d365848a5cb6107967298100a19cd08a6 Mon Sep 17 00:00:00 2001 From: John Otander <johnotander@gmail.com> Date: Fri, 5 Apr 2019 18:34:41 -0600 Subject: [PATCH 1/2] Implement first pass of MDXs This implements the basic idea of MDXs. Though we still need to figure out how to best handle the layout mechanism. cc/ @christopherbiscardi, @jxnblk, @timneutkens --- Related #454 --- packages/mdx/index.js | 3 +- packages/remark-mdxs/index.js | 76 +++++++++++++++++++ packages/remark-mdxs/license | 21 +++++ packages/remark-mdxs/package.json | 54 +++++++++++++ packages/remark-mdxs/readme.md | 55 ++++++++++++++ .../test/__snapshots__/test.js.snap | 41 ++++++++++ packages/remark-mdxs/test/test.js | 30 ++++++++ 7 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 packages/remark-mdxs/index.js create mode 100644 packages/remark-mdxs/license create mode 100644 packages/remark-mdxs/package.json create mode 100644 packages/remark-mdxs/readme.md create mode 100644 packages/remark-mdxs/test/__snapshots__/test.js.snap create mode 100644 packages/remark-mdxs/test/test.js diff --git a/packages/mdx/index.js b/packages/mdx/index.js index d5cdd889b..0feb733f8 100644 --- a/packages/mdx/index.js +++ b/packages/mdx/index.js @@ -30,6 +30,7 @@ function createMdxAstCompiler(options) { const fn = unified() .use(toMDAST, options) .use(remarkMdx, options) + .use(mdxHastToJsx, options) // Set JSX compiler early so it can be overridden .use(squeeze, options) .use(toMDXAST, options) @@ -81,8 +82,6 @@ function applyHastPluginsAndCompilers(compiler, options) { } }) - compiler.use(mdxHastToJsx, options) - for (const compilerPlugin of compilers) { compiler.use(compilerPlugin, options) } diff --git a/packages/remark-mdxs/index.js b/packages/remark-mdxs/index.js new file mode 100644 index 000000000..177f4d5b6 --- /dev/null +++ b/packages/remark-mdxs/index.js @@ -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 => [ +${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+/, '') + .replace(/;\s*$/, '') + : '"wrapper"' + }`, + '', + jsxFragments.join('\n\n'), + '', + defaultExport + ].join('\n') + } +} diff --git a/packages/remark-mdxs/license b/packages/remark-mdxs/license new file mode 100644 index 000000000..dc4d4beab --- /dev/null +++ b/packages/remark-mdxs/license @@ -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. diff --git a/packages/remark-mdxs/package.json b/packages/remark-mdxs/package.json new file mode 100644 index 000000000..76c653ea4 --- /dev/null +++ b/packages/remark-mdxs/package.json @@ -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 <johnotander@gmail.com> (https://johno.com)", + "contributors": [ + "John Otander <johnotander@gmail.com> (http://johnotander.com)", + "Brent Jackson <jacksonblack@gmail.com> (https://jxnblk.com)", + "Tim Neutkens <tim@zeit.co>", + "Matija Marohnić <matija.marohnic@gmail.com>", + "Titus Wormer <tituswormer@gmail.com> (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" + } +} diff --git a/packages/remark-mdxs/readme.md b/packages/remark-mdxs/readme.md new file mode 100644 index 000000000..4645b2e03 --- /dev/null +++ b/packages/remark-mdxs/readme.md @@ -0,0 +1,55 @@ +# [remark][]-[mdx][]s + +[![Build Status][build-badge]][build] +[![lerna][lerna-badge]][lerna] +[![Join the community on Spectrum][spectrum-badge]][spectrum] + +> :warning: This project is currently in alpha + +[MDXs][] syntax support for [remark][]. + +## Installation + +```sh +npm install --save remark-mdxs +``` + +## 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][] © [Titus Wormer][author] and [John Otander][author2] + +<!-- 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 + +[remark]: https://github.com/remarkjs/remark + +[mdx]: https://github.com/mdx-js/mdx + +[author]: https://wooorm.com + +[author2]: https://johno.com diff --git a/packages/remark-mdxs/test/__snapshots__/test.js.snap b/packages/remark-mdxs/test/__snapshots__/test.js.snap new file mode 100644 index 000000000..d528eae18 --- /dev/null +++ b/packages/remark-mdxs/test/__snapshots__/test.js.snap @@ -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" +`; diff --git a/packages/remark-mdxs/test/test.js b/packages/remark-mdxs/test/test.js new file mode 100644 index 000000000..cb25266c0 --- /dev/null +++ b/packages/remark-mdxs/test/test.js @@ -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, { + remarkPlugins: [remarkMdxs] + }) + + expect(result).toMatchSnapshot() +}) From 05eb9422cd604b247f5f3af98e6875f9081ba4cf Mon Sep 17 00:00:00 2001 From: John Otander <johnotander@gmail.com> Date: Wed, 10 Apr 2019 15:43:25 -0600 Subject: [PATCH 2/2] Improve readme, use compilers API --- packages/mdx/index.js | 3 ++- packages/remark-mdxs/readme.md | 36 +++++++++++++++++++++++-------- packages/remark-mdxs/test/test.js | 2 +- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/mdx/index.js b/packages/mdx/index.js index 0feb733f8..d5cdd889b 100644 --- a/packages/mdx/index.js +++ b/packages/mdx/index.js @@ -30,7 +30,6 @@ function createMdxAstCompiler(options) { const fn = unified() .use(toMDAST, options) .use(remarkMdx, options) - .use(mdxHastToJsx, options) // Set JSX compiler early so it can be overridden .use(squeeze, options) .use(toMDXAST, options) @@ -82,6 +81,8 @@ function applyHastPluginsAndCompilers(compiler, options) { } }) + compiler.use(mdxHastToJsx, options) + for (const compilerPlugin of compilers) { compiler.use(compilerPlugin, options) } diff --git a/packages/remark-mdxs/readme.md b/packages/remark-mdxs/readme.md index 4645b2e03..abff2fbe6 100644 --- a/packages/remark-mdxs/readme.md +++ b/packages/remark-mdxs/readme.md @@ -1,4 +1,4 @@ -# [remark][]-[mdx][]s +# @[mdx][]-js/mdxs [![Build Status][build-badge]][build] [![lerna][lerna-badge]][lerna] @@ -6,12 +6,34 @@ > :warning: This project is currently in alpha -[MDXs][] syntax support for [remark][]. +Multi-doc syntax support for MDX. ## Installation ```sh -npm install --save remark-mdxs +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 ``` ## Contribute @@ -24,7 +46,7 @@ abide by its terms. ## License -[MIT][] © [Titus Wormer][author] and [John Otander][author2] +[MIT][] © [John Otander][author] <!-- Definitions --> @@ -46,10 +68,6 @@ abide by its terms. [mit]: license -[remark]: https://github.com/remarkjs/remark - [mdx]: https://github.com/mdx-js/mdx -[author]: https://wooorm.com - -[author2]: https://johno.com +[author]: https://johno.com diff --git a/packages/remark-mdxs/test/test.js b/packages/remark-mdxs/test/test.js index cb25266c0..4319ba0ee 100644 --- a/packages/remark-mdxs/test/test.js +++ b/packages/remark-mdxs/test/test.js @@ -23,7 +23,7 @@ over here. it('correctly transpiles', async () => { const result = await mdx(FIXTURE, { - remarkPlugins: [remarkMdxs] + compilers: [remarkMdxs] }) expect(result).toMatchSnapshot()