Skip to content

Instantly share code, notes, and snippets.

@jxnblk
Last active February 10, 2018 17:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jxnblk/7c45b10d1ede88cc446741ea7254f87f to your computer and use it in GitHub Desktop.
Save jxnblk/7c45b10d1ede88cc446741ea7254f87f to your computer and use it in GitHub Desktop.
Proof of concept remark plugin for `.mdx` proposal https://spectrum.chat/thread/1021be59-2738-4511-aceb-c66921050b9a
const React = require('react')
const visit = require('unist-util-visit')
const toHAST = require('mdast-util-to-hast')
const toH = require('hast-to-hyperscript')
const babel = require('babel-core')
const frontmatter = require('remark-frontmatter')
const yaml = require('js-yaml')
const util = require('util')
const parse = raw => babel.transform(raw, {
plugins: [
[
require('babel-plugin-transform-es2015-modules-commonjs'),
{
strict: false,
loose: true
}
],
require('babel-plugin-transform-react-jsx')
]
}).code
const wrap = str => `<React.Fragment>${str.trim()}</React.Fragment>`
const toElement = (jsx = '', scope = {}) => {
const code = parse(wrap(jsx))
const keys = Object.keys(scope)
const values = keys.map(k => scope[k])
const fn = new Function('React', ...keys, `return ${code}`)
const el = fn(React, ...values)
return el
}
const mdx = function (opts) {
const pageScope = {
props: {},
}
let importCode = ''
this.use(frontmatter)
this.Compiler = ast => {
// visit(ast, node => { console.log('Node', node) })
visit(ast, 'yaml', yamlVisitor)
visit(ast, 'html', htmlVisitor)
const hast = {
type: 'element',
tagName: 'React.Fragment',
properties: {},
children: toHAST(ast, {
allowDangerousHTML: true,
handlers: {
html: (h, node) => {
if (Array.isArray(node.element.props.children)) {
const children = node.element.props.children.map(child => {
const type = pageScope[child.type] || child.type
return h(node, type, child.props, child.props.children)
})
const n = h(node, 'div', null, children)
return n
}
const { props } = node.element
const type = pageScope[node.element.type] || node.element.type
return h(node, type, props)
}
}
}).children
}
return toH(React.createElement, hast)
}
const yamlVisitor = node => {
const data = yaml.safeLoad(node.value)
pageScope.props = data
/*
if (data.imports) {
importCode += data.imports.join('\n')
}
*/
if (data.modules) {
Object.keys(data.modules).forEach(key => {
const req = require(data.modules[key])
const mod = req.default || req
pageScope[key] = req.default || req
// spread multiple exports
if (typeof mod === 'object') {
Object.keys(mod).forEach(key => {
pageScope[key] = mod[key]
})
}
})
}
}
const htmlVisitor = (node) => {
const scope = Object.assign({}, opts.components, pageScope)
node.element = toElement(node.value, scope)
}
}
module.exports = mdx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment