Skip to content

Commit

Permalink
feat(webpack): create webpack-loader to traverse MDAST
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroppy committed Jun 16, 2019
1 parent bf811c9 commit 1db5281
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 2 deletions.
14 changes: 14 additions & 0 deletions src/webpack/fusuma-loader.js
@@ -0,0 +1,14 @@
'use strict';

const mdx = require('@mdx-js/mdx');
const fusumaMdxPlugin = require('./fusumaMdxPlugin');

module.exports = function fusumaLoader(src) {
const cb = this.async();

const result = mdx.sync(src, {
remarkPlugins: [fusumaMdxPlugin]
});

cb(null, result);
};
110 changes: 110 additions & 0 deletions src/webpack/fusumaMdxPlugin.js
@@ -0,0 +1,110 @@
'use strict';

const visit = require('unist-util-visit');
const mdxAstToMdxHast = require('@mdx-js/mdx/mdx-ast-to-mdx-hast');
const { toJSX } = require('@mdx-js/mdx/mdx-hast-to-jsx');
const { parse } = require('@babel/parser');

function createFusumaProps(nodes) {
const property = {};

nodes.forEach(({ type, value }) => {
if (type === 'comment') {
const v = value.trim();

if (v === 'contents') {
property.contents = true;
}
if (v.slice(0, 4) === 'note') {
// sanitize
const escapeMap = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;'
// '/': '&#x2F;'
};

const s = v
.slice(4)
.trim()
.replace(/[&<>"']/gim, (m) => escapeMap[m])
.replace(/\n/g, '\\n');

property.note = s;
}
if (v.slice(0, 8) === 'classes:') {
property.classes = v.slice(8).trim();
}
if (v.slice(0, 13) === 'sectionTitle:') {
property.sectionTitle = v.slice(13).trim();
}
}
});

return `{${Object.entries(property)
.map(([key, value]) => `${key}: '${value}'`)
.join(',')}}`;
}

function fusumaMdxPlugin() {
return (tree, file) => {
const slides = [];
let slide = [];
const res = {
jsx: [],
fusumaProps: []
};

tree.children.forEach((n) => {
if (n.type === 'thematicBreak') {
slides.push(slide);
slide = [];
} else {
slide.push(n);
}
});

// push last slide
slides.push(slide);

slides.forEach((slide) => {
const hash = mdxAstToMdxHast()({
type: 'root',
children: slide
});
const mdxJSX = toJSX(hash);

// jsx variable is established, so we don't use babel/parser
const jsx = mdxJSX.match(/<MDXLayout.+?>([\s\S]*)<\/MDXLayout>/m);

if (jsx) {
const template = `
(props) => (
<>
${jsx[1]}
</>
)`;
const fusumaProps = createFusumaProps(slide);

res.jsx.push(template);
res.fusumaProps.push(fusumaProps);
}
});

// we have to transform one src to an array because we separate slides using ---
// Identifier 'slides' has already been declared
tree.children.push({
type: 'export',
default: false,
value: `
import React from 'react';
import { mdx } from '@mdx-js/react';
export const slides = [${res.jsx.join(',\n')}];
export const fusumaProps = [${res.fusumaProps.join(',\n')}];`
});
};
}

module.exports = fusumaMdxPlugin;
10 changes: 8 additions & 2 deletions src/webpack/webpack.config.js
Expand Up @@ -41,8 +41,14 @@ module.exports = ({ meta, slide, extends: fileExtends, internal }) => {
}
},
{
test: /\.md$/,
use: ['html-loader', 'markdown-loader']
test: /\.mdx?$/,
use: [
{
loader: 'babel-loader',
options: require('../configs/babelrc')(code)
},
path.join(__dirname, './fusuma-loader.js')
]
},
{
test: /\.(png|jpg|gif|svg?)$/,
Expand Down

0 comments on commit 1db5281

Please sign in to comment.