diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..43c97e71 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..d13265ad --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM mhart/alpine-node + +WORKDIR /usr/src + +COPY package.json . +RUN npm i --ignore-scripts + +COPY . . +RUN npm run docs:build && mv dist /public diff --git a/docs/_app.js b/docs/_app.js new file mode 100644 index 00000000..379c5882 --- /dev/null +++ b/docs/_app.js @@ -0,0 +1,82 @@ +import React from 'react' +import RebassMDX from '@rebass/mdx' +import createScope from '@rebass/markdown' +import * as Rebass from 'rebass' +import sortBy from 'lodash.sortby' +import { SidebarLayout, ScopeProvider } from '@compositor/x0/components' + +import { LiveEditor, Logo } from './_ui' + +const scope = { ...createScope(), ...Rebass, code: LiveEditor, pre: ({ children }) => children } + +const navOrder = [ + 'index', + 'syntax', + 'getting-started', + 'webpack', + 'parcel', + 'next', + 'create-react-app', + 'gatsby', + 'x0', + 'typescript', + 'plugins', + 'advanced', + 'ast', + 'components', + 'writing-a-plugin', + 'custom-loader', + 'specification', + 'sync-api', + 'runtime', + 'contributing', + 'projects', + 'about' +] + +const pageNames = { + index: 'Introduction', + next: 'Next.js', + ast: 'AST', + projects: 'Projects Using MDX', + 'getting-started': 'Getting Started', + 'create-react-app': 'Create React App', + 'writing-a-plugin': 'Writing a Plugin', + 'sync-api': 'Sync API' +} + +const sortRoutes = routes => [ + ...sortBy([...routes], a => { + const i = navOrder.indexOf(a.name) + return i < 0 ? Infinity : i + }) +].map(route => { + if (!pageNames[route.name]) return route + return { + ...route, + name: pageNames[route.name] + } +}) + +export default class App extends React.Component { + static defaultProps = { + title: 'MDX' + } + + render () { + const { routes } = this.props + const nav = sortRoutes(routes) + + return ( + + + } + routes={nav} + /> + + + ) + } +} diff --git a/docs/_ui.js b/docs/_ui.js new file mode 100644 index 00000000..e53ef82e --- /dev/null +++ b/docs/_ui.js @@ -0,0 +1,38 @@ +import React from 'react' +import { Pre, Box, Border } from 'rebass' +import { LiveEditor as Editor, LivePreview } from '@compositor/x0/components' + +export const Logo = () => + +export const LiveEditor = props => { + const lang = (props.className || '').replace(/^language\-/, '') + const type = lang.charAt(0) + const code = React.Children.toArray(props.children).join('\n') + + switch (type) { + case '.': + return ( + + ) + case '!': + return ( + + ) + default: + return ( +
+      )
+  }
+}
diff --git a/docs/about.md b/docs/about.md
new file mode 100644
index 00000000..25cb32f6
--- /dev/null
+++ b/docs/about.md
@@ -0,0 +1,54 @@
+import { Avatar } from 'rebass'
+
+# About
+
+MDX is based on the [original `.mdx` proposal](https://spectrum.chat/thread/1021be59-2738-4511-aceb-c66921050b9a) by Guillermo Rauch ([@rauchg](https://twitter.com/rauchg)).
+
+## Design
+
+[Logo designs](https://github.com/mdx-js/design) were created by [Evil Rabbit](https://twitter.com/evilrabbit_) of [ZEIT](https://zeit.co).
+
+[See logo resources](https://github.com/mdx-js/design)
+
+## Authors
+
+- [John Otander](https://johno.com) ([@4lpine](https://twitter.com/4lpine)) – [Compositor](https://compositor.io) + [Clearbit](https://clearbit.com)
+- Tim Neutkens ([@timneutkens](https://github.com/timneutkens)) – [ZEIT](https://zeit.co)
+- [Guillermo Rauch](https://rauchg.com) ([@rauchg](https://twitter.com/rauchg)) – [ZEIT](https://zeit.co)
+- [Brent Jackson](https://jxnblk.com) ([@jxnblk](https://twitter.com/jxnblk)) – [Compositor](https://compositor.io)
+
+## Related
+
+The following projects, languages, and articles helped to shape MDX either in implementation or inspiration.
+
+### Syntax
+
+These projects define the syntax which MDX blends together (MD and JSX).
+
+- [Markdown](https://daringfireball.net/projects/markdown/syntax)
+- [JSX](https://reactjs.org/docs/introducing-jsx.html)
+- [React](https://reactjs.org/)
+
+### Parsing and implementation
+
+- [Remark](http://remark.js.org)
+- [Unified](https://github.com/unifiedjs/unified)
+- [Webpack](https://webpack.js.org)
+- [Parcel](https://parceljs.com)
+
+### Libraries
+
+- [MDXC](https://github.com/jamesknelson/mdxc)
+- [Markdown Component Loader](https://github.com/ticky/markdown-component-loader)
+- [markdown-in-js](https://github.com/threepointone/markdown-in-js)
+- [remark-jsx](https://github.com/fazouane-marouane/remark-jsx)
+- [remark-react](https://github.com/mapbox/remark-react)
+
+### Other
+
+- [IA Markdown Content Blocks](https://github.com/iainc/Markdown-Content-Blocks)
+- [Idyll: Markup language for interactive documents](https://idyll-lang.org)
+
+### Is your work missing?
+
+If you have related work or prior art we've failed to reference, please open a PR!
diff --git a/docs/advanced/ast.md b/docs/advanced/ast.md
new file mode 100644
index 00000000..9b6f430e
--- /dev/null
+++ b/docs/advanced/ast.md
@@ -0,0 +1,118 @@
+# AST
+
+The majority of the MDXAST specification is defined by [MDAST][].
+MDXAST is a superset of MDAST, with three additional node types:
+
+- `jsx` (in place of `html`)
+- `import`
+- `export`
+
+It's also important to note that an MDX document that contains no JSX or imports is a valid MDAST.
+
+### Differences to MDAST
+
+The `import` type is used to provide the necessary block elements to the Remark HTML block parser and for the execution context/implementation.
+For example, a webpack [loader][] might want to transform an MDX import by appending those imports.
+
+An `export` is used to emit data from MDX, similarly to traditional markdown frontmatter.
+
+The `jsx` node would most likely be passed to Babel to create functions.
+
+This will also differ a bit in parsing because the remark parser is built to handle particular HTML element types, whereas JSX support will require the ability to parse any tag, and those that self close.
+
+The `jsx`, `import`, and `export` node types are defined below.
+
+#### JSX
+
+The `JSX` ([`ElementNode`](#elementnode)) which contains embedded JSX as a string and `children` ([`ElementNode`](#elementnode)).
+
+```idl
+interface JSX <: Element {
+  type: "jsx";
+  value: "string";
+  children: [ElementNode]
+}
+```
+
+For example, the following MDX:
+
+```jsx
+
+  Hello, world!
+
+```
+
+Yields:
+
+```json
+{
+  "type": "jsx",
+  "value": "\n  Hello, world!\n"
+}
+```
+
+#### Import
+
+The `import` ([`Textnode`](#textnode)) contains the raw import as a string.
+
+```idl
+interface JSX <: Text {
+  type: "import";
+}
+```
+
+For example, the following MDX:
+
+```md
+import Video from '../components/Video'
+```
+
+Yields:
+
+```json
+{
+  "type": "import",
+  "value": "import Video from '../components/Video'"
+}
+```
+
+#### Export
+
+The `export` ([`Textnode`](#textnode)) contains the raw export as a string.
+
+```idl
+interface JSX <: Text {
+  type: "export";
+}
+```
+
+For example, the following MDX:
+
+```md
+export { foo: 'bar' }
+```
+
+Yields:
+
+```json
+{
+  "type": "export",
+  "value": "export { foo: 'bar' }"
+}
+```
+
+## MDXHAST
+
+The majority of the MDXHAST specification is defined by [HAST][].
+MDXHAST is a superset of HAST, with four additional node types:
+
+- `jsx`
+- `import`
+- `export`
+- `inlineCode`
+
+It's also important to note that an MDX document that contains no JSX or imports results in a valid HAST.
+
+[MDAST]: https://github.com/syntax-tree/mdast
+[HAST]: https://github.com/syntax-tree/hast
+[loader]: https://github.com/mdx-js/mdx/tree/master/packages/loader
diff --git a/docs/advanced/components.md b/docs/advanced/components.md
new file mode 100644
index 00000000..eb20491c
--- /dev/null
+++ b/docs/advanced/components.md
@@ -0,0 +1,41 @@
+# Components
+
+The MDX core library accepts a string and exports a JSX string.
+
+## MDXTag
+
+MDXTag is an internal component that MDX uses to map components to an HTML element based on the Markdown syntax.
+Consider the following MDX:
+
+```
+import MyComponent from './my-component'
+
+# Title
+
+
+
+Lorem ipsum dolor sit amet.
+```
+
+MDX core turns that text into the following JSX to be consumed by your app:
+
+```jsx
+import React from 'react'
+import { MDXTag } from '@mdx-js/tag'
+import MyComponent from './my-component'
+
+export default ({ components }) => (
+  
+    
+      Title
+    
+    
+    
+      Lorem ipsum dolor sit amet.
+    
+  
+)
+```
+
+If the component mapping contains a `p` key, that will be used for "Lorem ipsum dolor sit amet.", otherwise a standard `p` tag is rendered (`

Lorem ipsum dolor sit amet.

`). +This is what allows you to pull in existing components to style your MDX documents. diff --git a/docs/advanced/contributing.md b/docs/advanced/contributing.md new file mode 100644 index 00000000..caf619b7 --- /dev/null +++ b/docs/advanced/contributing.md @@ -0,0 +1,20 @@ +# Contributing + +:pray: Thanks in advance for your contribution! + +To get started, after cloning the repo: + +``` +npm i +npm t +``` + +## Project structure + +MDX is a monorepo that uses [lerna][]. + +- All packages are found in `./packages` +- All documentation is found in `./docs` and can be viewed with `npm run docs -- -o` +- There's an `./examples` directory where examples for different tools and frameworks + +[lerna]: https://lernajs.io diff --git a/docs/advanced/custom-loader.md b/docs/advanced/custom-loader.md new file mode 100644 index 00000000..94aa7d62 --- /dev/null +++ b/docs/advanced/custom-loader.md @@ -0,0 +1,7 @@ +import { Message } from 'rebass' + +# Custom loader + + + This docs page is a WIP + diff --git a/docs/advanced/index.md b/docs/advanced/index.md new file mode 100644 index 00000000..39ff28f3 --- /dev/null +++ b/docs/advanced/index.md @@ -0,0 +1,7 @@ +import { Message } from 'rebass' + +# Advanced + + + This docs page is a WIP + diff --git a/docs/advanced/runtime.md b/docs/advanced/runtime.md new file mode 100644 index 00000000..b4ff5e47 --- /dev/null +++ b/docs/advanced/runtime.md @@ -0,0 +1,3 @@ +import Doc from '../../packages/runtime/readme.md' + + diff --git a/docs/advanced/specification.md b/docs/advanced/specification.md new file mode 100644 index 00000000..51332cfd --- /dev/null +++ b/docs/advanced/specification.md @@ -0,0 +1,11 @@ +# MDX Specification + +MDX includes a [specification][spec] to define the syntax and transpilation. +This can be leveraged by code formatters, linters, and implementations in other languages created by the community. +It's based on the [remark][remark]/[unist][unist] ecosystem to ensure robust parsing and the ability to leverage plugins from within your MDX. + +[See the specification][spec] + +[spec]: https://github.com/mdx-js/specification +[remark]: https://github.com/remarkjs +[unified]: https://github.com/unifiedjs diff --git a/docs/advanced/sync-api.md b/docs/advanced/sync-api.md new file mode 100644 index 00000000..0de523cf --- /dev/null +++ b/docs/advanced/sync-api.md @@ -0,0 +1,23 @@ +import { Message } from 'rebass' + +# Sync API + +MDX processes everything asynchronously by default. +In certain cases this behavior might not be desirable. + +If you're using the MDX library directly, you might want to process an MDX string synchronously. +It's important to note that if you have any async plugins, they will be ignored. + +```js +const fs = require('fs') +const mdx = require('@mdx-js/mdx') + +const mdxText = fs.readFileSynx('hello.mdx', 'utf8') + +const jsx = mdx.sync(mdxText) +``` + +MDX's [runtime][] package has [example][] usage. + +[runtime]: https://github.com/mdx-js/mdx/tree/master/packages/runtime +[example]: https://github.com/mdx-js/mdx/blob/d5a5189e715dc28370de13f6cc0fd18a06f0f122/packages/runtime/src/index.js#L16-L18 diff --git a/docs/advanced/writing-a-plugin.md b/docs/advanced/writing-a-plugin.md new file mode 100644 index 00000000..24b80926 --- /dev/null +++ b/docs/advanced/writing-a-plugin.md @@ -0,0 +1,7 @@ +import { Message } from 'rebass' + +# Writing a plugin + + + This docs page is a WIP + diff --git a/docs/getting-started/create-react-app.md b/docs/getting-started/create-react-app.md new file mode 100644 index 00000000..34bfebab --- /dev/null +++ b/docs/getting-started/create-react-app.md @@ -0,0 +1,41 @@ +import { Message } from 'rebass' + +# Create React App + + + This docs page is a WIP + + +With Create React App you will need to use [`create-react-app-rewired`][cra-rewired] and add a `config-overrides.js`. + +```js +const { getBabelLoader } = require('react-app-rewired') +module.exports = (config, env) => { + const babelLoader = getBabelLoader(config.module.rules) + config.module.rules.map(rule => { + if (typeof rule.test !== 'undefined' || typeof rule.oneOf === 'undefined') { + return rule + } + + rule.oneOf.unshift({ + test: /\.mdx$/, + use: [ + { + loader: babelLoader.loader, + options: babelLoader.options + }, + '@mdx-js/loader' + ] + }) + + return rule + }) + return config +} +``` + +[See the full example][cra-example] + +[cra]: https://github.com/facebook/create-react-app +[cra-rewired]: https://github.com/timarney/react-app-rewired +[cra-example]: https://github.com/mdx-js/mdx/tree/master/examples/create-react-app diff --git a/docs/getting-started/gatsby.md b/docs/getting-started/gatsby.md new file mode 100644 index 00000000..aaff6759 --- /dev/null +++ b/docs/getting-started/gatsby.md @@ -0,0 +1,35 @@ +# Gatsby + +In order to use MDX with [Gatsby][gatsby] you can use the [gatsby-mdx][] package. + +First scaffold a new Gatsby 2.0 or greater site and install the `gatsby-mdx` plugin. + +```shell +gatsby new gatsby-site https://github.com/gatsbyjs/gatsby-starter-default#v2 +cd gatsby-site +yarn add gatsby-mdx @mdx-js/mdx +``` + +Then add `gatsby-mdx` to your `gatsby-config.js` in the `plugins` section. + +```javascript +module.exports = { + siteMetadata: { + title: `My Ambitious Project` + }, + plugins: [`gatsby-mdx`] +}; +``` + +Finally, add an `.mdx` file in the `src/pages` directory. It "Just Works". + +``` +# My first MDX Page + +some awesome content +``` + +For more documentation on programmatically creating pages with Gatsby, see the [gatsby-mdx docs][gatsby-mdx] + +[gatsby]: https://gatsbyjs.org +[gatsby-mdx]: https://github.com/ChristopherBiscardi/gatsby-mdx diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md new file mode 100644 index 00000000..ac49d7d4 --- /dev/null +++ b/docs/getting-started/index.md @@ -0,0 +1,84 @@ +import { Text } from 'rebass' + +# Getting Started + +To get started quickly with an example project you can use `npm init`. +It will scaffold out a [Next.js][next] app with MDX configured. + +``` +npm init mdx +``` + + + Note: MDX requires a version of node that is >= v8.5 and React 16.0+ + + +## Components + +You can pass in components for any HTML element that Markdown compiles to. +This allows you to use your existing components and even CSS-in-JS like `styled-components`. + +```jsx +import React from 'react' +import Hello from '../hello.md' + +import { + Text, + Heading, + Code, + InlineCode +} from '../ui' + +export default () => + +``` + +With the above, the Heading component will be rendered for any h1, Text for p tags, and so on. + +In addition to HTML elements, there's an `inlineCode`. +This is what remark uses for code elements within paragraphs, tables, etc. + +## MDXProvider + +If you're using an app layout that wraps your JSX, you can use the `MDXProvider` to only pass your components in one place: + +```jsx +import React from 'react' +import { MDXProvider } from '@mdx-js/tag' + +import * as components from './markdown-components' + +export default props => + +
+ +``` + +This allows you to remove duplicated component imports and passing. +It will typically go in layout files. + +#### How does it work? + +MDXProvider uses React [context][] to provide the component mapping to MDXTag. +MDXTag knows to use these components when determining which to render. + +## Projects, libraries and frameworks + +If you're already working with a particular tool, you can try out MDX with the following commands: + +- `npm init mdx` [`webpack`](./webpack) +- `npm init mdx` [`parcel`](./parcel) +- `npm init mdx` [`next`](./next) +- `npm init mdx` [`create-react-app`](./create-react-app) +- `npm init mdx` [`gatsby`](./gatsby) +- `npm init mdx` [`x0`](./x0) + +[next]: https://github.com/zeit/next.js +[context]: https://reactjs.org/docs/context.html diff --git a/docs/getting-started/next.md b/docs/getting-started/next.md new file mode 100644 index 00000000..476ae3bf --- /dev/null +++ b/docs/getting-started/next.md @@ -0,0 +1,31 @@ +# Next.js + +Next.js provides an [official plugin][next-plugin] to simplify MDX importing into your project. + +``` +npm install --save-dev @zeit/next-mdx +``` + +To configure MDX, add the following to your `next.config.js`: + +```js +const withMDX = require('@zeit/next-mdx')() + +module.exports = withMDX() +``` + +### Use MDX for `.md` files + +The Next.js MDX plugin allows for you to also use MDX parsing for `.md` files: + +```js +const withMDX = require('@zeit/next-mdx')({ + extension: /\.mdx?$/ +}) + +module.exports = withMDX({ + pageExtensions: ['js', 'jsx', 'md', 'mdx'] +}) +``` + +[next-plugin]: https://github.com/zeit/next-plugins/tree/master/packages/next-mdx diff --git a/docs/getting-started/parcel.md b/docs/getting-started/parcel.md new file mode 100644 index 00000000..87557142 --- /dev/null +++ b/docs/getting-started/parcel.md @@ -0,0 +1,26 @@ +import { Message } from 'rebass' + +# Parcel + + + This docs page is a WIP + + +You'll need to install the `@mdx-js/parcel-plugin-mdx` plugin to transpile MDX. + +```js +{ + "scripts": { + "start": "parcel index.html --no-cache" + }, + "dependencies": { + "react": "16.4.1", + "react-dom": "16.4.1", + "@mdx-js/tag": "@mdx-js/tag" + }, + "devDependencies": { + "@mdx-js/parcel-plugin-mdx": "@mdx-js/parcel-plugin-mdx", + "parcel-bundler": "1.9.0" + } +} +``` diff --git a/docs/getting-started/typescript.md b/docs/getting-started/typescript.md new file mode 100644 index 00000000..126ef40e --- /dev/null +++ b/docs/getting-started/typescript.md @@ -0,0 +1,11 @@ +# TypeScript + +If you're getting errors from TypeScript related to imports with an `*.mdx` extension, create an `mdx.d.ts` file in your types directory and include inside your `tsconfig.json` + +``` +// types/mdx.d.ts +declare module '*.mdx' { + let MDXComponent: (props) => JSX.Element; + export default MDXComponent; +} +``` diff --git a/docs/getting-started/webpack.md b/docs/getting-started/webpack.md new file mode 100644 index 00000000..1403757e --- /dev/null +++ b/docs/getting-started/webpack.md @@ -0,0 +1,26 @@ +import { Message } from 'rebass' + +# Webpack + + + This docs page is a WIP + + +## Basic Setup + +MDX provides a loader that needs to be used in tandem with the [babel-loader][babel-loader]. + +For webpack projects you can define the following `webpack.config.js`: + +```js +module.exports = { + module: { + rules: [ + { + test: /\.mdx?$/, + use: ['babel-loader', '@mdx-js/loader'] + } + ] + } +} +``` diff --git a/docs/getting-started/x0.md b/docs/getting-started/x0.md new file mode 100644 index 00000000..9622ed60 --- /dev/null +++ b/docs/getting-started/x0.md @@ -0,0 +1,27 @@ +import { Message } from 'rebass' + +# x0 + + + This docs page is a WIP + + +[x0][] supports MDX files with either `.md` or `.mdx` file extensions out of the box, but you will need to use the component provider in `_app.js`. Here's an example using [Rebass][rebass] components: + +```jsx +import React from 'react' +import * as Rebass from 'rebass' +import createScope from '@rebass/markdown' +import { ScopeProvider } from '@compositor/x0/components' + +export default ({ route, routes, ...props }) => ( + + + + + +) +``` + +[x0]: https://compositor.io/x0 +[rebass]: https://jxnblk.com/rebass diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..9454291c --- /dev/null +++ b/docs/index.md @@ -0,0 +1,56 @@ +import { Border, Blockquote, BlockLink } from 'rebass' + +# MDX + +MDX enables you to seamlessly use JSX in your Markdown documents. +This makes writing long-form content with components a blast. + +__:heart: Powerful__: MDX makes it effortless to import and render components in your React/JSX-based projects. + +__:computer: Component-based__: Use existing JSX components inside your MDX, and import MDX files as plain components. + +__:fire: Blazingly blazing fast__: MDX has no runtime, all compilation occurs during the build stage. + +> “It's extremely useful for using design system components to render markdown +and weaving interactive components in with existing markdown.” +> +> — [@chrisbiscardi](https://twitter.com/chrisbiscardi/status/1022304288326864896) + +## Why? + +Before MDX, some of the benefits of writing Markdown were lost when integrating with JSX. +Implementations were often template string-based which required lots of escaping and cumbersome syntax. + +MDX seeks to make writing with Markdown _and_ JSX simpler while being more expressive. +The possibilities are endless when you combine components (that can even be dynamic or load data) with the simplicity of Markdown for long-form content. + +## Features + +- Fast +- No runtime compilation +- [Pluggable][remark-plugins] +- Element to React component mapping +- React component `import`/`export` +- Customizable layouts +- Webpack loader +- Parcel plugin +- Next.js plugin +- Gatsby plugin + +> [Watch some of these features in action](https://www.youtube.com/watch?v=d2sQiI5NFAM&list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u) + +## Try it + +```.mdx +# Hello, world! + +Here's an example of the [Rebass][] Donut rendered inside an MDX document. + + + +[Rebass]: https://jxnblk.com/rebass +``` + +[md]: http://commonmark.org/ +[jsx]: https://facebook.github.io/jsx/ +[remark-plugins]: https://github.com/remarkjs/remark/blob/master/doc/plugins.md diff --git a/docs/plugins.md b/docs/plugins.md new file mode 100644 index 00000000..deac5836 --- /dev/null +++ b/docs/plugins.md @@ -0,0 +1,82 @@ +# Plugins + +Since MDX uses the [remark][]/[rehype][] ecosystems, you can use plugins to modify the AST at different stages of the transpilation process. + +## Transpilation + +The MDX transpilation flow consists of six steps, ultimately resulting in JSX that can be used in React/Preact/etc. + +1. __Parse__: Text => MDAST +1. __Transpile__: MDAST => MDXAST +1. __Transform__: MDX/Remark plugins applied to AST +1. __Transpile__: MDXAST => MDXHAST +1. __Transform__: Hyperscript plugins applied to AST +1. __Transpile__: MDXHAST => JSX + +### Options + +Name | Type | Required | Description +---- | ---- | -------- | ----------- +`mdPlugins` | Array[] | `false` | Array of remark plugins to manipulate the MDAST +`hastPlugins` | Array[] | `false` | Array of rehype plugins to manipulate the MDXHAST + +#### Specifying plugins + +Plugins need to be passed to MDX core library, this is often as options to your loader: + +```js +const images = require('remark-images') +const emoji = require('remark-emoji') + +module.exports = { + module: { + rules: [ + { + test: /\.mdx?$/, + use: [ + { + loader: 'babel-loader' + }, + { + loader: '@mdx-js/loader', + options: { + mdPlugins: [images, emoji] + } + } + ] + } + ] + } +} +``` + +Though if you're using MDX directly, they can be passed like so: + +```js +const fs = require('fs') +const mdx = require('@mdx-js/mdx') +const images = require('remark-images') +const emoji = require('remark-emoji') + +const mdxText = fs.readFileSync('hello.mdx', 'utf8') +const jsx = mdx.sync(mdxText, { + mdPlugins: [images, emoji] +}) +``` + +#### Plugin options + +If a plugin needs specific options, use the `[plugin, pluginOptions]` syntax. + +```js +mdx.sync(mdxText, { + mdPlugins: [ + images, + [emoji, { padSpaceAfter: true }] +}) +``` + +The following example ensures that `padSpaceAfter` is only passed as options to the `emoji` plugin. + +[remark]: https://github.com/remarkjs/remark +[rehype]: https://github.com/rehypejs/rehype diff --git a/docs/projects.md b/docs/projects.md new file mode 100644 index 00000000..b7b836e0 --- /dev/null +++ b/docs/projects.md @@ -0,0 +1,16 @@ +# Projects using MDX + +- [ok-mdx][]: Browser-based MDX editor +- [docz][]: Documentation framework +- [mdx-deck][]: MDX-based presentation decks + +## Sites using MDX + +- [ZEIT Docs][zeit-docs] +- [Compositor][compositor] + +[ok-mdx]: https://github.com/jxnblk/ok-mdx +[mdx-deck]: https://github.com/jxnblk/mdx-deck +[docz]: https://www.docz.site/ +[zeit-docs]: https://github.com/zeit/docs +[compositor]: https://compositor.io diff --git a/docs/syntax.md b/docs/syntax.md new file mode 100644 index 00000000..1b980e9e --- /dev/null +++ b/docs/syntax.md @@ -0,0 +1,125 @@ +# Syntax + +MDX syntax can be boiled down to being JSX in Markdown. +It's a superset of Markdown syntax that also support importing, exporting, and JSX blocks. + +### Markdown + +Standard [Markdown syntax][md] is supported. + +### JSX + +[JSX syntax][jsx] is fully supported, JSX blocks are opened by starting a line with the `<` character. + +```jsx + + Here's a JSX block + It's pretty neat + +``` + +### Imports + +Imports can be used to [import][] components into the scope and later rendered: + +```jsx +import Graph from './components/graph' + +## Here's a graph + + +``` + +You can also import data that you want to display in a JSX block: + +```jsx +import { colors } from './theme' +import Palette from './components/palette' + +# Colors + + +``` + +#### Markdown file transclusion + +You can [transclude][] Markdown files by importing one `.md` or `.mdx` file into another: + +```jsx +import License from './license.md' +import Contributing from './docs/contributing.md' + +# Hello, world! + + + +--- + + +``` + +### Exports + +You can use exports to export metadata like layout or authors. +It's a mechanism for an imported MDX file to communicate with its parent. +It works similarly to frontmatter, but uses ES2015 syntax. + +```js +// posts/post.mdx +import { fred, sue } from '../data/authors' +import Layout from '../components/blog-layout' + +export const meta = { + authors: [fred, sue], + layout: Layout +} + +# Post about MDX + +MDX is a JSX in Markdown loader, parser, and renderer for ambitious projects. +``` + +```jsx +// index.js +import React from 'react' +import Mdx, { meta } from 'posts/post.mdx' + +const { authors, layout } = meta + +export default () => ( + + + By: {authors.map(author => author.name)} + +) +``` + +#### `export default` + +The ES default [export][] is used to provide a layout component which will wrap the transpiled JSX. + +You can export it as a function: + +```jsx +import Layout from './Layout' + +export default ({ children }) => {children} + +# Hello, world! +``` + +Or directly as a component: + +```jsx +import Layout from './Layout' + +export default Layout + +# Hello, world! +``` + +[md]: https://daringfireball.net/projects/markdown/syntax +[jsx]: https://reactjs.org/docs/introducing-jsx.html +[import]: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/import +[export]: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export +[transclude]: https://en.wikipedia.org/wiki/Transclusion diff --git a/now.json b/now.json new file mode 100644 index 00000000..ec4c3b7d --- /dev/null +++ b/now.json @@ -0,0 +1,6 @@ +{ + "name": "mdx", + "alias": "mdxjs.com", + "public": true, + "type": "static" +} diff --git a/package.json b/package.json index 852257a4..ed005351 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,9 @@ "private": true, "scripts": { "bootstrap": "lerna bootstrap", + "docs": "x0 docs", + "docs:build": "x0 build docs", + "docs:deploy": "npm run docs:build && now dist", "test": "lerna run test", "format": "prettier --no-semi --single-quote --write '{examples,packages}/**/*.js'", "prepare": "lerna bootstrap && lerna link --force-local" @@ -9,7 +12,15 @@ "repository": "mdx-js/mdx", "license": "MIT", "devDependencies": { - "lerna": "^2.5.1", - "prettier": "^1.11.1" + "@compositor/x0": "^6.0.3", + "lerna": "^2.11.0", + "prettier": "^1.13.7" + }, + "dependencies": { + "@rebass/mdx": "^1.0.0-1", + "styled-components": "^3.3.3" + }, + "x0": { + "title": "MDX" } } diff --git a/readme.md b/readme.md index ad58ee05..584c171f 100644 --- a/readme.md +++ b/readme.md @@ -3,360 +3,52 @@ [![Build Status](https://travis-ci.org/mdx-js/mdx.svg?branch=master)](https://travis-ci.org/mdx-js/mdx) [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/mdx) -MDX is a JSX in Markdown loader, parser, and renderer for ambitious projects. -It combines the readability of Markdown with the expressivity of JSX. -The best of both worlds. :globe_with_meridians: +MDX enables you to seamlessly use JSX in your Markdown documents. +This makes writing long-form content with components a blast. -[See the MDX specification](https://github.com/mdx-js/specification) +__:heart: Powerful__: MDX makes it effortless to import and render components in your React/JSX-based projects. -## Features +__:computer: Component-based__: Use existing JSX components inside your MDX, and import MDX files as plain components. -- Fast -- No runtime compilation -- [Pluggable](https://github.com/remarkjs/remark/blob/master/doc/plugins.md) -- Element to React component mapping -- React component `import`/`export` -- Simpler image syntax -- Webpack loader +__:fire: Blazingly blazing fast__: MDX has no runtime, all compilation occurs during the build stage. > [Watch some of these features in action](https://www.youtube.com/watch?v=d2sQiI5NFAM&list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u) -## Installation +> “It's extremely useful for using design system components to render markdown +and weaving interactive components in with existing markdown.” +> +> — [@chrisbiscardi](https://twitter.com/chrisbiscardi/status/1022304288326864896) -```sh -npm install --save-dev @mdx-js/loader @mdx-js/mdx -``` +## Why? -> Note: mdx requires a version of node that is >= `v8.5` +Before MDX, some of the benefits of writing Markdown were lost when integrating with JSX. +Implementations were often template string-based which required lots of escaping and cumbersome syntax. -### Configuring with Webpack +MDX seeks to make writing with Markdown _and_ JSX simpler while being more expressive. +The possibilities are endless when you combine components (that can even be dynamic or load data) with the simplicity of Markdown for long-form content. -You'll need to specify the `@mdx-js/loader` webpack loader and follow it with the `babel-loader`: +- Fast +- No runtime compilation +- [Pluggable][remark-plugins] +- Element to React component mapping +- React component `import`/`export` +- Customizable layouts +- Webpack loader +- Parcel plugin +- Next.js plugin +- Gatsby plugin -```js -module.exports = { - module: { - rules: [ - { - test: /\.md$/, - use: ['babel-loader', '@mdx-js/loader'] - } - ] - } -} -``` - -

-

- Examples - -
-

- -### Configuring with Parcel - -You'll need to install the `@mdx-js/parcel-plugin-mdx` plugin to transpile MDX. - -```js -{ - "scripts": { - "start": "parcel index.html --no-cache" - }, - "dependencies": { - "react": "16.4.1", - "react-dom": "16.4.1", - "@mdx-js/tag": "latest" - }, - "devDependencies": { - "@mdx-js/parcel-plugin-mdx": "latest", - "parcel-bundler": "1.9.0" - } -} -``` - -

-

- Examples - -
-

- -### Configuring TypeScript - -If you're getting errors from TypeScript related to imports with an `*.mdx` extension, create an `mdx.d.ts` file in your types directory and include inside your `tsconfig.json` +## Getting started ``` -// types/mdx.d.ts -declare module '*.mdx' { - let MDXComponent: (props) => JSX.Element; - export default MDXComponent; -} +npx init mdx ``` -## Usage - -Create an md file, `hello.md`: - -```jsx -# Hello, world! -``` - -And import it from a component, `pages/index.js`: - -```jsx -import React from 'react' -import Hello from '../hello.md' - -export default () => -``` - -## MDX syntax - -### Imports - -Similarly to JSX, components can be rendered after an `import`: - -```jsx -import Graph from './components/graph' - -## Here's a graph - - -``` - -#### Markdown file transclusion - -You can transclude Markdown files by importing one `.md` file into another: - -```jsx -import License from './license.md' -import Contributing from './docs/contributing.md' - -# Hello, world! - - - ---- - - -``` - -### Exports - -You can use exports to export metadata like layout or authors. -It's a mechanism for an imported MDX file to communicate with its parent. -It works similarly to frontmatter, but uses ES2015 syntax. - -```js -// posts/post.mdx -import { fred, sue } from '../data/authors' -import Layout from '../components/blog-layout' - -export const meta = { - authors: [fred, sue], - layout: Layout -} - -# Post about MDX - -MDX is a JSX in Markdown loader, parser, and renderer for ambitious projects. -``` - -```jsx -// index.js -import React from 'react' -import Mdx, { meta } from 'posts/post.mdx' - -const { authors, layout } = meta - -export default () => ( - - - By: {authors.map(author => author.name)} - -) -``` - -#### `export default` - -The ES default export is used to provide a layout component which will wrap the transpiled JSX. - -You can export it as a function: - -```jsx -import Layout from './Layout' - -export default ({children}) => {children} - -# Hello, world! -``` - -Or directly as a component: - -```jsx -import Layout from './Layout' - -export default Layout - -# Hello, world! -``` - -Any additional props passed to the imported MDX component will be passed to its default export: - -```jsx -// index.js -import Mdx from './posts/post.mdx' - -export default () => ( - -) -``` -```js -// posts/post.mdx -import Layout from '../components/blog-layout' - -# Hello, world! - -export default ({ children, someProp }) => {children} -``` - -### Component customization - -You can pass in components for any `html` element that Markdown compiles to. -This allows you to use your existing components and use CSS-in-JS like `styled-components`. - -```jsx -import React from 'react' -import Hello from '../hello.md' - -import { - Text, - Heading, - Code, - InlineCode -} from '../ui' - -export default () => - -``` - -### MDXProvider - -If you're using an app layout that wraps your JSX, you can use the `MDXProvider` to only pass your components in one place: - -```jsx -import React from 'react' -import { MDXProvider } from '@mdx-js/tag' - -import * as components from './markdown-components' - -export default props => - -
- -``` - -## Plugins - -Since MDX uses the [remark](https://github.com/remarkjs/remark)/[rehype](https://github.com/rehypejs/rehype) ecosystems, you can use plugins to modify the AST at different stages of the transpilation process. - -If you look at the [next example](https://github.com/mdx-js/mdx/blob/master/examples/next/next.config.js#L3), it shows you to pass plugins as options to the [MDX loader](https://github.com/mdx-js/mdx/tree/master/packages/loader). - -### Options - -Name | Type | Required | Description ----- | ---- | -------- | ----------- -`mdPlugins` | Array[] | `false` | Array of remark plugins to manipulate the MDXAST -`hastPlugins` | Array[] | `false` | Array of rehype plugins to manipulate the MDXHAST -`skipExport` | Boolean | `false` | Skip `export default` wrapper on transpiled JSX - -#### Specifying plugins - -Plugins need to be passed to the MDX loader via webpack options. - -If a plugin needs specific options, use the `[plugin, pluginOptions]` syntax. - -```js -const images = require('remark-images') -const emoji = require('remark-emoji') -const toc = require('remark-toc') - -module.exports = { - module: { - rules: [ - { - test: /\.mdx?$/, - use: [ - { - loader: 'babel-loader' - }, - { - loader: '@mdx-js/loader', - options: { - mdPlugins: [images, emoji, [toc, {heading: 'intro'}]] - } - } - ] - } - ] - } -} -``` - -##### Next.js - -If you're using Next.js, you can specify options in your `next.config.js`. - -```js -const images = require('remark-images') -const emoji = require('remark-emoji') - -module.exports = { - pageExtensions: ['js', 'jsx', 'md', 'mdx'], - webpack: (config, { defaultLoaders }) => { - config.module.rules.push({ - test: /\.md$/, - use: [ - defaultLoaders.babel, - { - loader: '@mdx-js/loader', - options: { - mdPlugins: [images, emoji] - } - } - ] - }) - - return config - } -} -``` - -## Sync API - -If you're using the MDX library directly, you might want to process an MDX string synchronously. - -```js -const jsx = mdx.sync('# Hello, world!') -``` +- [Documentation](https://mdxjs.com) + - [Syntax](https://mdxjs.com/syntax) + - [Getting Started](https://mdxjs.com/getting-started) + - [Plugins](https://mdxjs.com/plugins) + - [Contributing](https://mdxjs.com/advanced/contributing) ## Related @@ -368,3 +60,7 @@ const jsx = mdx.sync('# Hello, world!') - Tim Neutkens ([@timneutkens](https://github.com/timneutkens)) – [ZEIT](https://zeit.co) - Guillermo Rauch ([@rauchg](https://twitter.com/rauchg)) – [ZEIT](https://zeit.co) - Brent Jackson ([@jxnblk](https://twitter.com/jxnblk)) – [Compositor](https://compositor.io) + +--- + +> [MIT](./license) license