mirror of
https://github.com/jxnblk/mdx-deck.git
synced 2024-11-25 15:50:39 +03:00
Init
This commit is contained in:
commit
c9157a37d9
43
README.md
Normal file
43
README.md
Normal file
@ -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
|
||||
<CodeSnippet />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
<Demo />
|
||||
|
||||
---
|
||||
|
||||
# The end
|
||||
|
||||
````
|
||||
|
||||
```sh
|
||||
mdx-deck deck.mdx
|
||||
```
|
||||
|
||||
[MDX]: https://github.com/mdx-js/mdx
|
17
cli.js
Executable file
17
cli.js
Executable file
@ -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)
|
||||
})
|
23
docs/index.mdx
Normal file
23
docs/index.mdx
Normal file
@ -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
|
||||
|
||||
---
|
||||
|
||||
<Box p={3} bg='magenta'>
|
||||
And you can import React components!
|
||||
</Box>
|
||||
|
140
lib/dev.js
Normal file
140
lib/dev.js
Normal file
@ -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
|
||||
}) => `<!DOCTYPE html>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
<style>*{box-sizing:border-box}body{font-family:system-ui,sans-serif;margin:0}</style>
|
||||
<div id=root></div>
|
||||
${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
|
102
lib/entry.js
Normal file
102
lib/entry.js
Normal file
@ -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 (
|
||||
<div>
|
||||
<Carousel innerRef={this.root}>
|
||||
{slides.map((Component, i) => (
|
||||
<Slide key={i} id={'slide-' + i}>
|
||||
<Component />
|
||||
</Slide>
|
||||
))}
|
||||
</Carousel>
|
||||
<samp>{this.state.index + 1}/{this.state.length}</samp>
|
||||
<button onClick={e => this.update(dec)}>previous</button>
|
||||
<button onClick={e => this.update(inc)}>next</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
render(<App slides={slides} />, window.root)
|
||||
|
||||
if (module.hot) module.hot.accept()
|
32
lib/loader.js
Normal file
32
lib/loader.js
Normal file
@ -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)
|
||||
}
|
29
package.json
Normal file
29
package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user