Skip to content

Commit

Permalink
feat(docz-core): improve plugin and add support to modify babel
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck committed May 27, 2018
1 parent 2bd3b5b commit cf3ec4e
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 70 deletions.
17 changes: 4 additions & 13 deletions packages/docz-core/src/Bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,16 @@ export class Bundler<C = any> {
}

public async createServer(config: C): Promise<BundlerServer> {
const { plugins } = this.args
const server = await this.server(config)

if (plugins && plugins.length > 0) {
for (const plugin of plugins) {
await plugin.bundlerServer(server)
}
}

return server
return this.server(config)
}

public async build(): Promise<void> {
this.builder(this.config)
public async build(config: C): Promise<void> {
await this.builder(config)
}

private reduceWithPlugins(dev: boolean): any {
return (config: C, plugin: Plugin) =>
plugin.bundlerConfig(config, dev) || config
plugin.modifyBundlerConfig(config, dev) || config
}

private mountConfig(config: C): C {
Expand Down
13 changes: 7 additions & 6 deletions packages/docz-core/src/Entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ import * as glob from 'fast-glob'
import * as path from 'path'

import * as paths from './config/paths'
import { propOf } from './utils/helpers'
import { touch, compiled, readIfExist } from './utils/fs'

import { Entry, parseMdx } from './Entry'
import { Plugin } from './Plugin'
import { Config } from './commands/args'

const fromTemplates = (file: string) => path.join(paths.templates, file)
const fromDocz = (file: string) => path.join(paths.docz, file)

const writeAppFiles = async (config: Config, dev: boolean): Promise<void> => {
const { plugins, title, description, theme } = config
const props = Plugin.propsOfPlugins(plugins)

const wrappers = propOf(plugins, 'wrapper')
const afterRenders = propOf(plugins, 'afterRender')
const beforeRenders = propOf(plugins, 'beforeRender')
const wrappers = props('wrapper')
const onPreRenders = props('onPreRender')
const onPostRenders = props('onPostRender')

const root = await compiled(fromTemplates('root.tpl.js'))
const js = await compiled(fromTemplates('index.tpl.js'))
Expand All @@ -33,8 +34,8 @@ const writeAppFiles = async (config: Config, dev: boolean): Promise<void> => {
})

const rawIndexJs = js({
afterRenders,
beforeRenders,
onPreRenders,
onPostRenders,
})

const rawIndexHtml = html({
Expand Down
82 changes: 61 additions & 21 deletions packages/docz-core/src/Plugin.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,77 @@
import get from 'lodash.get'

import { isFn } from './utils/helpers'

export type BundlerConfig = <Config>(config: Config, dev: boolean) => Config
export type BundlerServer = <Server>(server: Server) => void
export type BeforeRender = () => void
export type AfterRender = () => void
export type Wrapper = <R>(props: { children: any }) => R
export interface BabelRC {
presets?: any[]
plugins?: any[]
cacheDirectory?: boolean
babelrc?: boolean
}

export type ModifyBundlerConfig = <C>(config: C, dev: boolean) => C
export type ModifyBabelRC = (babelrc: BabelRC) => BabelRC
export type OnServerListening = <S>(server: S) => void
export type OnPreBuild = () => void
export type OnPostBuild = () => void
export type OnPreRender = () => void
export type OnPostRender = () => void
export type Wrapper = <R>(props: any) => R

export interface PluginFactory {
bundlerConfig: BundlerConfig
bundlerServer: BundlerServer
beforeRender: BeforeRender
afterRender: AfterRender
modifyBundlerConfig: ModifyBundlerConfig
modifyBabelRc: ModifyBabelRC
onServerListening: OnServerListening
onPreBuild: OnPreBuild
onPostBuild: OnPostBuild
onPreRender: OnPreRender
onPostRender: OnPostRender
wrapper: Wrapper
}

export class Plugin {
public readonly bundlerConfig: BundlerConfig
public readonly bundlerServer: BundlerServer
public readonly beforeRender: BeforeRender
public readonly afterRender: AfterRender
public static runPluginsMethod(
plugins: Plugin[]
): (method: keyof Plugin, ...args: any[]) => void {
return (method, ...args) => {
if (plugins && plugins.length > 0) {
for (const plugin of plugins) {
const fn = get(plugin, method)
isFn(fn) && fn(...args)
}
}
}
}

public static propsOfPlugins(
plugins: Plugin[] | undefined
): (prop: keyof Plugin) => any {
return prop =>
plugins &&
plugins.length > 0 &&
plugins.map(p => get(p, prop)).filter(m => m)
}

public readonly modifyBundlerConfig: ModifyBundlerConfig
public readonly modifyBabelRc: ModifyBabelRC
public readonly onServerListening: OnServerListening
public readonly onPreBuild: OnPreBuild | null
public readonly onPostBuild: OnPostBuild | null
public readonly onPreRender: OnPreRender | null
public readonly onPostRender: OnPostRender | null
public readonly wrapper: Wrapper

constructor(p: PluginFactory) {
this.bundlerConfig = (config: any, dev: boolean) => {
return isFn(p.bundlerConfig) && p.bundlerConfig(config, dev)
}

this.bundlerServer = async (server: any) => {
isFn(p.bundlerServer) && (await p.bundlerServer(server))
this.modifyBundlerConfig = (config: any, dev: boolean) => {
return isFn(p.modifyBundlerConfig) && p.modifyBundlerConfig(config, dev)
}

this.beforeRender = p.beforeRender
this.afterRender = p.afterRender
this.modifyBabelRc = p.modifyBabelRc
this.onServerListening = p.onServerListening
this.onPreBuild = p.onPreBuild
this.onPostBuild = p.onPostBuild
this.onPreRender = p.onPreRender
this.onPostRender = p.onPostRender
this.wrapper = p.wrapper
}
}
Expand Down
18 changes: 6 additions & 12 deletions packages/docz-core/src/bundlers/webpack/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import * as path from 'path'
import { load } from 'load-cfg'
import merge from 'deepmerge'
import webpackBarPlugin from 'webpackbar'
import Config from 'webpack-chain'
import HappyPack from 'happypack'
Expand All @@ -13,6 +11,7 @@ import matter from 'remark-frontmatter'
import { Config as ConfigObj } from '../../commands/args'
import { plugin as mdastPlugin } from '../../utils/plugin-mdast'
import { plugin as hastPlugin } from '../../utils/plugin-hast'
import { BabelRC } from '../../Plugin'
import { Env } from './'

const INLINE_LIMIT = 10000
Expand All @@ -22,14 +21,6 @@ interface HappypackLoaderParams {
plugins?: any[]
}

const getBabelRc = (debug: boolean) =>
merge(load('babel', null), {
babelrc: false,
cacheDirectory: !debug,
presets: [require.resolve('babel-preset-react-app')],
plugins: [],
})

const happypackLoader = (babelrc: any) => ({
id,
plugins,
Expand Down Expand Up @@ -70,7 +61,10 @@ const setupHappypack = (config: Config, babelrc: any) => {
config.plugin('happypack-mdx').use(HappyPack, mdx)
}

export const createConfig = (args: ConfigObj, env: Env): Config => {
export const createConfig = (babelrc: BabelRC) => (
args: ConfigObj,
env: Env
): Config => {
const { paths, debug } = args

const srcPath = path.resolve(paths.root, args.src)
Expand Down Expand Up @@ -273,7 +267,7 @@ export const createConfig = (args: ConfigObj, env: Env): Config => {
* plugins
*/

setupHappypack(config, getBabelRc(args.debug))
setupHappypack(config, babelrc)

config.plugin('assets-plugin').use(manifestPlugin, [
{
Expand Down
6 changes: 4 additions & 2 deletions packages/docz-core/src/bundlers/webpack/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Configuration as CFG } from 'webpack'

import { Bundler } from '../../Bundler'
import { babelrc } from '../../utils/babelrc'
import { Config as Args } from '../../commands/args'
import { createConfig } from './config'
import { server } from './server'
Expand All @@ -9,12 +10,13 @@ import { build } from './build'
export type Env = 'production' | 'development'

export const bundler = (args: Args, env: Env): Bundler<CFG> => {
const config: any = createConfig(args, env).toConfig()
const create = createConfig(babelrc(args))
const config: any = create(args, env).toConfig()

return new Bundler({
args,
config,
build,
config,
server: server(args),
})
}
12 changes: 11 additions & 1 deletion packages/docz-core/src/commands/build.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import * as fs from 'fs-extra'
import logger from 'signale'

import * as paths from '../config/paths'
import { loadConfig } from '../utils/load-config'
import { webpack } from '../bundlers'
import { Entries } from '../Entries'
import { Config } from './args'
import { Plugin } from '../Plugin'

export const build = async (args: Config) => {
const config = loadConfig(args)
const bundler = webpack(config, 'production')
const entries = new Entries(config)
const map = await entries.getMap()
const run = Plugin.runPluginsMethod(config.plugins)

await fs.remove(paths.app)
await Entries.writeApp(config)
await Entries.writeImports(map)
await Entries.writeData(map, config)

await bundler.build()
try {
await run('onPreBuild')
await bundler.build(bundler.getConfig())
await run('onPostBuild')
} catch (err) {
logger.fatal(err)
process.exit(1)
}
}
12 changes: 8 additions & 4 deletions packages/docz-core/src/commands/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Config } from './args'
import { DataServer } from '../DataServer'
import { webpack } from '../bundlers'
import { Entries } from '../Entries'
import { Plugin } from '../Plugin'
import { loadConfig } from '../utils/load-config'

process.env.BABEL_ENV = process.env.BABEL_ENV || 'development'
Expand All @@ -15,13 +16,14 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'
export const dev = async (args: Config) => {
const config = loadConfig(args)
const bundler = webpack(config, 'development')
const server = await bundler.createServer(bundler.getConfig())
const app = await server.start()
const entries = new Entries(config)
const map = await entries.getMap()
const server = await bundler.createServer(bundler.getConfig())
const app = await server.start()

app.on('listening', async ({ server }) => {
const port = await detectPort(config.websocketPort)
const run = Plugin.runPluginsMethod(config.plugins)
const newConfig = { ...config, websocketPort: port }
const dataServer = new DataServer({ server, config: newConfig })

Expand All @@ -33,11 +35,13 @@ export const dev = async (args: Config) => {
await Entries.writeApp(newConfig, true)
await Entries.writeImports(map)

logger.info('Setup entries socket')
logger.info(`Setup entries socket on port ${port}`)
await dataServer.processEntries(entries)
await dataServer.processThemeConfig()
await run('onServerListening', server)
} catch (err) {
logger.fatal('Failed to process your docs:', err)
logger.fatal('Failed to process your server:', err)
process.exit(1)
}
})
}
22 changes: 22 additions & 0 deletions packages/docz-core/src/utils/babelrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { load } from 'load-cfg'
import merge from 'deepmerge'

import { Config } from '../commands/args'
import { isFn } from './helpers'

export const babelrc = (args: Config) => {
const config = merge(load('babel', null), {
babelrc: false,
cacheDirectory: !args.debug,
presets: [require.resolve('babel-preset-react-app')],
plugins: [],
})

return [...(args.plugins || [])].reduce(
(obj, plugin) =>
isFn(plugin.modifyBabelRc)
? merge(obj, plugin.modifyBabelRc(config))
: obj,
config
)
}
3 changes: 2 additions & 1 deletion packages/docz-core/src/utils/format.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as prettier from 'prettier'
import logger from 'signale'

export const format = (code: string): Promise<string> =>
new Promise((resolve, reject) => {
Expand All @@ -11,7 +12,7 @@ export const format = (code: string): Promise<string> =>

resolve(result)
} catch (err) {
console.log(err)
logger.fatar(err)
resolve(err)
}
})
4 changes: 0 additions & 4 deletions packages/docz-core/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ export function pick<R = object>(props: string[], obj: any): R {
return newObj as R
}

export function propOf<T>(arr: any[] | undefined, method: keyof T): any {
return arr && arr.map(p => p[method]).filter(m => m)
}

export function isArrEqual(arr: any[] | null, to: any[] | null): boolean {
if (!arr || !to || arr.length !== to.length) {
return false
Expand Down
12 changes: 6 additions & 6 deletions packages/docz-core/templates/index.tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import ReactDOM from 'react-dom'
import { imports } from './imports'
import Root from './root'

const _beforeRenders = [<% if (beforeRenders) {%><%- beforeRenders %><%}%>]
const _afterRenders = [<% if (afterRenders) {%><%- afterRenders %><%}%>]
const _onPreRenders = [<% if (onPreRenders) {%><%- onPreRenders %><%}%>]
const _onPostRenders = [<% if (onPostRenders) {%><%- onPostRenders %><%}%>]

const beforeRender = () => _beforeRenders.forEach(f => f && f())
const afterRender = () => _afterRenders.forEach(f => f && f())
const onPreRender = () => _onPreRenders.forEach(f => f && f())
const onPostRender = () => _onPostRenders.forEach(f => f && f())

const root = document.querySelector('#root')
const render = (Component = Root) => {
beforeRender()
ReactDOM.render(<Component imports={imports} />, root, afterRender)
onPreRender()
ReactDOM.render(<Component imports={imports} />, root, onPostRender)
}

if (module.hot) {
Expand Down

0 comments on commit cf3ec4e

Please sign in to comment.