Skip to content

Commit

Permalink
feat(docz-core): pass theme config via websockets for dev
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck committed May 23, 2018
1 parent c49fc64 commit 5222de7
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 29 deletions.
11 changes: 7 additions & 4 deletions packages/docz-core/src/commands/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ export interface Argv {
/* io args */
src: string
files: string
/* template args */
title: string
description: string
theme: string
/* bundler args */
env: string
debug: boolean
Expand All @@ -17,13 +13,20 @@ export interface Argv {
port: number
websocketPort: number
websocketHost: string
/* template args */
title: string
description: string
theme: string
}

export interface Config extends Argv {
paths: Paths
plugins?: Plugin[]
mdPlugins: any[]
hastPlugins: any[]
themeConfig?: {
[key: string]: any
}
}

export const args = (yargs: any) => {
Expand Down
71 changes: 53 additions & 18 deletions packages/docz-core/src/commands/dev.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { load } from 'load-cfg'
import { load, finds } from 'load-cfg'
import chokidar from 'chokidar'
import WebSocket from 'ws'
import WS from 'ws'

import * as paths from '../config/paths'
import { Config } from './args'
Expand All @@ -11,25 +11,29 @@ import { webpack } from '../bundlers'
process.env.BABEL_ENV = process.env.BABEL_ENV || 'development'
process.env.NODE_ENV = process.env.NODE_ENV || 'development'

const isSocketOpened = (socket: WS) => socket.readyState === WS.OPEN

const entriesData = (entries: EntryMap, config: Config) =>
JSON.stringify({ type: 'entries data', data: entries })
JSON.stringify({ type: 'docz.entries', data: entries })

const updateEntries = (socket: WebSocket) => (config: Config) => async () => {
const newEntries = new Entries(config)
const newMap = await newEntries.getMap()
const updateEntries = (socket: WS) => (config: Config) => async () => {
if (isSocketOpened(socket)) {
const newEntries = new Entries(config)
const newMap = await newEntries.getMap()

await Entries.rewrite(newMap)
socket.send(entriesData(newMap, config))
await Entries.rewrite(newMap)
socket.send(entriesData(newMap, config))
}
}

const processEntries = (config: Config) => async (ws: WebSocket.Server) => {
const processEntries = (config: Config) => async (ws: WS.Server) => {
const entries = new Entries(config)
const map = await entries.getMap()
const watcher = chokidar.watch(config.files, {
ignored: /(^|[\/\\])\../,
})

const handleConnection = async (socket: WebSocket) => {
const handleConnection = async (socket: WS) => {
const update = updateEntries(socket)

socket.send(entriesData(map, config))
Expand All @@ -44,24 +48,54 @@ const processEntries = (config: Config) => async (ws: WebSocket.Server) => {

ws.on('connection', handleConnection)
ws.on('close', () => watcher.close())

await Entries.write(config, map)
}

const INITIAL_CONFIG = {
paths,
plugins: [],
mdPlugins: [],
hastPlugins: [],
const configData = (config: Config) =>
JSON.stringify({ type: 'docz.config', data: config.themeConfig })

const updateConfig = (socket: WS, args: Config) => () => {
const config = load('docz', {}, true)

if (isSocketOpened(socket)) {
socket.send(configData(config))
}
}

const processThemeConfig = (config: Config) => async (ws: WS.Server) => {
const watcher = chokidar.watch(finds('docz'))

const handleConnection = async (socket: WS) => {
const update = updateConfig(socket, config)

watcher.on('add', update)
watcher.on('change', update)
watcher.on('unlink', update)

update()
}

ws.on('connection', handleConnection)
ws.on('close', () => watcher.close())
}

export const dev = async (args: Config) => {
const config = load('docz', { ...args, ...INITIAL_CONFIG })
const config = load('docz', {
...args,
paths,
plugins: [],
mdPlugins: [],
hastPlugins: [],
themeConfig: {},
})

const bundler = webpack(config)
const server = await bundler.createServer(bundler.getConfig())
const app = await server.start()

app.on('listening', async server => {
const ws = new WebSocket.Server({
const ws = new WS.Server({
server,
host: config.websocketHost,
port: config.websocketPort,
Expand All @@ -75,6 +109,7 @@ export const dev = async (args: Config) => {
process.on('exit', handleClose)
process.on('SIGINT', handleClose)

await processEntries(args)(ws)
await processEntries(config)(ws)
await processThemeConfig(config)(ws)
})
}
10 changes: 8 additions & 2 deletions packages/docz-core/templates/app.tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ const Wrapper = props =>

class App extends React.Component {
state = {
config: {},
entries: {},
imports: {}
imports: {},
}

static getDerivedStateFromProps(nextProps, prevState) {
return {
config: prevState.config,
entries: prevState.entries,
imports: nextProps.imports
}
Expand All @@ -31,9 +33,13 @@ class App extends React.Component {
socket.onmessage = ev => {
const message = JSON.parse(ev.data)

if (message.type === 'entries data') {
if (message.type === 'docz.entries') {
this.setState({ entries: message.data })
}

if (message.type === 'docz.config') {
this.setState({ config: message.data })
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/docz/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
},
"dependencies": {
"@sindresorhus/slugify": "^0.3.0",
"deepmerge": "^2.1.0",
"invariant": "^2.2.4",
"loadable-components": "^2.1.0",
"pascalcase": "^0.1.1",
Expand All @@ -37,6 +38,7 @@
},
"devDependencies": {
"@types/bluebird": "^3.5.20",
"@types/deepmerge": "^2.1.0",
"@types/react": "^16.3.12",
"@types/react-dom": "^16.0.5",
"@types/react-router-dom": "^4.2.6"
Expand Down
25 changes: 20 additions & 5 deletions packages/docz/src/theme.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react'
import { ComponentType as CT } from 'react'
import { BrowserRouter } from 'react-router-dom'
import merge from 'deepmerge'

export type MSXComponent = CT<{
components: { [key: string]: any }
Expand All @@ -20,17 +21,23 @@ export interface Entry {
order: number
}

export interface ThemeConfig {
[key: string]: any
}

export type EntryMap = Record<string, Entry>
export type ImportMap = Record<string, () => Promise<MSXImport>>

export interface DataContext {
imports: ImportMap
config: ThemeConfig
entries: EntryMap
imports: ImportMap
}

const initialContext: DataContext = {
imports: {},
config: {},
entries: {},
imports: {},
}

export const dataContext = React.createContext(initialContext)
Expand All @@ -40,12 +47,20 @@ export interface ThemeProps extends DataContext {
children(WrappedComponent: CT): JSX.Element
}

export function theme(WrappedComponent: CT): CT<ThemeProps> {
export function theme(
WrappedComponent: CT,
defaultConfig?: ThemeConfig
): CT<ThemeProps> {
const Theme: CT<ThemeProps> = props => {
const { wrapper: Wrapper } = props
const { wrapper: Wrapper, entries, imports, config = {} } = props
const value = {
entries,
imports,
config: merge(defaultConfig || {}, config),
}

return (
<dataContext.Provider value={props}>
<dataContext.Provider value={value}>
<BrowserRouter>
<Wrapper>
<WrappedComponent />
Expand Down

0 comments on commit 5222de7

Please sign in to comment.