commit c9157a37d9c76548f0bc05a2fd4d9bee304e841f Author: Brent Jackson Date: Sat Jul 28 14:21:36 2018 -0400 Init diff --git a/README.md b/README.md new file mode 100644 index 0000000..c773456 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ + +# mdx-deck + +Create presentation decks with [MDX][] + +```sh +npm i mdx-deck +``` + +````md +--- +imports: + - import Demo from './components/Demo' +--- + +# This is the title of my deck + +--- + +# About Me + +--- + +```jsx + +``` + +--- + + + + +--- + +# The end + +```` + +```sh +mdx-deck deck.mdx +``` + +[MDX]: https://github.com/mdx-js/mdx diff --git a/cli.js b/cli.js new file mode 100755 index 0000000..3472c9d --- /dev/null +++ b/cli.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node + +const open = require('react-dev-utils/openBrowser') +const dev = require('./lib/dev') + +const opts = { +} + +dev(opts) + .then(res => { + const url = 'http://localhost:' + res.port + open(url) + console.log(url) + }) + .catch(err => { + console.error(err) + }) diff --git a/docs/index.mdx b/docs/index.mdx new file mode 100644 index 0000000..51398a1 --- /dev/null +++ b/docs/index.mdx @@ -0,0 +1,23 @@ +--- +imports: + - import Box from 'superbox' +--- + +# Hello + +--- + +# This is a presentation deck! + +--- + +# Built with [MDX][] + +[MDX]: https://github.com/mdx-js/mdx + +--- + + + And you can import React components! + + diff --git a/lib/dev.js b/lib/dev.js new file mode 100644 index 0000000..a0c006f --- /dev/null +++ b/lib/dev.js @@ -0,0 +1,140 @@ +const path = require('path') +const webpack = require('webpack') +const Koa = require('koa') +const getPort = require('get-port') +const koaWebpack = require('koa-webpack') +const HTMLPlugin = require('mini-html-webpack-plugin') +const ProgressBarPlugin = require('progress-bar-webpack-plugin') +const chalk = require('chalk') + +const devMiddleware = { + publicPath: '/', + clientLogLevel: 'error', + stats: 'errors-only', + logLevel: 'error', +} + +const babel = { + presets: [ + 'babel-preset-env', + 'babel-preset-stage-0', + 'babel-preset-react', + ].map(require.resolve) +} + +const rules = [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: require.resolve('babel-loader'), + options: babel + }, + { + test: /\.js$/, + exclude: path.resolve(__dirname, '../node_modules'), + include: [ + path.resolve(__dirname, '..'), + ], + loader: require.resolve('babel-loader'), + options: babel + }, + { + test: /\.mdx?$/, + exclude: /node_modules/, + use: [ + { + loader: require.resolve('babel-loader'), + options: babel + }, + require.resolve('./loader.js'), + ] + } +] + +const template = ({ + js, + publicPath +}) => ` + + +
+${HTMLPlugin.generateJSReferences(js, publicPath)} +` + +const config = { + stats: 'errors-only', + mode: 'development', + module: { + rules + }, + resolve: { + modules: [ + path.relative(process.cwd(), path.join(__dirname, '../node_modules')), + 'node_modules' + ] + }, + plugins: [ + new HTMLPlugin({ + template + }), + new ProgressBarPlugin({ + width: '24', + complete: '█', + incomplete: chalk.gray('░'), + format: [ + chalk.magenta('[ok] :bar'), + chalk.magenta(':percent'), + chalk.gray(':elapseds :msg'), + ].join(' '), + // summaryContent: chalk.magenta('[ok] done '), + summary: false, + }) + ] +} + +const start = async (opts = {}) => { + const app = new Koa() + // const dirname = path.dirname(opts.entry) + const hotPort = await getPort() + const hotClient = { + port: hotPort, + logLevel: 'error' + } + + // config.context = dirname + + config.resolve.modules.push( + // dirname, + // path.join(dirname, 'node_modules') + ) + + config.entry = [ + // path.join(__dirname, './overlay.js'), + path.join(__dirname, './entry.js') + ] + + config.plugins.push( + new webpack.DefinePlugin({ + OPTIONS: JSON.stringify(opts), + // APP_FILENAME: JSON.stringify(opts.entry), + HOT_PORT: JSON.stringify(hotPort) + }) + ) + + const middleware = await koaWebpack({ + config, + devMiddleware, + hotClient + }) + const port = opts.port || await getPort() + app.use(middleware) + + const server = app.listen(port) + return new Promise((resolve) => { + middleware.devMiddleware.waitUntilValid(() => { + resolve({ server, app, middleware, port }) + }) + }) +} + +module.exports = start diff --git a/lib/entry.js b/lib/entry.js new file mode 100644 index 0000000..b9e1523 --- /dev/null +++ b/lib/entry.js @@ -0,0 +1,102 @@ +import React from 'react' +import { render } from 'react-dom' +import PropTypes from 'prop-types' +import { MDXProvider } from '@mdx-js/tag' +import styled from 'styled-components' +import Box from 'superbox' +import throttle from 'lodash.throttle' +import debounce from 'lodash.debounce' + +// todo: dynamic import +import slides from '../docs/index.mdx' + +const Carousel = styled.div([], { + display: 'flex', + overflowX: 'auto', + width: '100vw', +}) +const Slide = styled.div([], { + outline: '2px solid tomato', + flex: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + flexDirection: 'column', + overflow: 'hidden', + width: '100vw', + height: '90vh' +}) + +const inc = state => ({ index: (state.index + 1) % state.length }) +const dec = state => state.index > 0 + ? ({ index: (state.index - 1) % state.length }) + : null + +class App extends React.Component { + static propTypes = { + slides: PropTypes.array.isRequired, + } + + state = { + length: this.props.slides.length, + index: 0 + } + + root = React.createRef() + + update = fn => this.setState(fn) + + handleScroll = debounce(e => { + if (this.isProgrammaticScroll) return + const { scrollLeft } = e.target + const rect = e.target.getBoundingClientRect() + const n = Math.round(scrollLeft / rect.width) + console.log('scroll', e, n) + this.setState({ index: n }) + }, 1000) + + componentDidMount () { + this.root.current.addEventListener('scroll', this.handleScroll) + } + + componentWillUnmount () { + this.root.current.removeEventListener('scroll', this.handleScroll) + } + + componentDidUpdate () { + if (!this.root.current) return + const { index } = this.state + const el = this.root.current.querySelector('#slide-' + index) + if (!el) return + this.isProgrammaticScroll = true + el.scrollIntoView({ + behavior: 'smooth' + }) + setTimeout(() => { + console.log(this.isProgrammaticScroll) + this.isProgrammaticScroll = false + }, 1000) + } + + render () { + const { slides } = this.props + return ( +
+ + {slides.map((Component, i) => ( + + + + ))} + + {this.state.index + 1}/{this.state.length} + + +
+ ) + } +} + +render(, window.root) + +if (module.hot) module.hot.accept() diff --git a/lib/loader.js b/lib/loader.js new file mode 100644 index 0000000..c9efc98 --- /dev/null +++ b/lib/loader.js @@ -0,0 +1,32 @@ +const mdx = require('@mdx-js/mdx') +const matter = require('gray-matter') + +const EXREG = /export\sdefault\s/g + +module.exports = async function (src) { + const callback = this.async() + + const { data, content } = matter(src) + + // todo: hoist imports + const slides = content.split('---\n') + .map(str => mdx.sync(str)) + .map(str => str.trim()) + .map(str => str.replace(EXREG, '')) + + console.log(data) + const { + imports = [] + } = data + + const code = `import React from 'react' +import { MDXTag } from '@mdx-js/tag' +${imports.join('\n')} + +export default [ + ${slides.join(',\n\n')} +]` + // console.log(code) + + return callback(null, code) +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5940718 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "mdx-deck", + "version": "1.0.0-0", + "description": "", + "main": "index.js", + "scripts": { + "start": "./cli.js" + }, + "keywords": [], + "author": "Brent Jackson", + "license": "MIT", + "dependencies": { + "@mdx-js/tag": "^0.14.1", + "chalk": "^2.4.1", + "debounce": "^1.1.0", + "get-port": "^4.0.0", + "gray-matter": "^4.0.1", + "koa": "^2.5.2", + "koa-webpack": "^5.1.0", + "lodash.throttle": "^4.1.1", + "mini-html-webpack-plugin": "^0.2.3", + "ok-cli": "^2.0.9", + "progress-bar-webpack-plugin": "^1.11.0", + "prop-types": "^15.6.2", + "react-dev-utils": "^5.0.1", + "styled-components": "^3.3.3", + "superbox": "^2.1.0" + } +}