Skip to content

Commit

Permalink
feat(docz-core): add chokidar to watch new entries
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck committed Apr 16, 2018
1 parent 206a67f commit 2f073d5
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 124 deletions.
91 changes: 6 additions & 85 deletions packages/docz-core/src/Bundler.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,7 @@
import * as fs from 'fs'
import * as path from 'path'
import * as mkdir from 'mkdirp'
import * as prettier from 'prettier'
import { compile } from 'art-template'
import del from 'del'

import * as paths from './config/paths'
import { Entry } from './Entry'
import { Plugin, IPluginFactory } from './Plugin'
import { Plugin } from './Plugin'
import { ConfigArgs } from './Server'

const mkd = (dir: string): void => {
try {
fs.lstatSync(dir)
} catch (err) {
mkdir.sync(dir)
}
}

const format = (raw: string) =>
prettier.format(raw, {
semi: false,
singleQuote: true,
trailingComma: 'all',
})

const touch = (file: string, raw: string) => {
const content = /js/.test(path.extname(file)) ? format(raw) : raw

mkd(paths.docz)
fs.writeFileSync(file, content, 'utf-8')
}

const compiled = (templateFile: string) =>
compile(fs.readFileSync(`${paths.templatesPath}/${templateFile}`, 'utf-8'))

export type TConfigFn<C> = (entries: Entry[]) => C
export type TConfigFn<C> = () => C
export type TCompilerFn<C> = (config: C) => Promise<any>
export type TServerFn<S> = (compiler: any) => S

Expand All @@ -50,10 +16,6 @@ export interface IBundlerConstructor<C, S> extends ICompilerOpts {
server: TServerFn<S>
}

const app = compiled('app.tpl.js')
const js = compiled('index.tpl.js')
const html = compiled('index.tpl.html')

export class Bundler<C = any, S = any> {
readonly id: string
readonly args: ConfigArgs
Expand All @@ -76,60 +38,19 @@ export class Bundler<C = any, S = any> {
plugin.bundlerConfig(config, dev) || config
}

private mountConfig(entries: Entry[]) {
private mountConfig() {
const { plugins, env } = this.args

const dev = env === 'development'
const initialConfig = this.config(entries)
const initialConfig = this.config()

return Boolean(plugins) && plugins.length > 0
? plugins.reduce(this.reduceWithPlugins(dev), initialConfig)
: initialConfig
}

private routesFromEntries(entries: Entry[]) {
return (
entries &&
entries.length > 0 &&
entries.reduce((obj, entry) => {
return Object.assign({}, obj, { [entry.name]: entry.route })
}, {})
)
}

private propOfPlugins(method: keyof IPluginFactory) {
const { plugins } = this.args
return plugins && plugins.map(p => p[method]).filter(m => m)
}

private generateFilesByTemplate(entries: Entry[]) {
const { theme } = this.args

touch(paths.indexHtml, html({}))

touch(
paths.appJs,
app({
THEME: theme,
ENTRIES: entries,
ROUTES: JSON.stringify(this.routesFromEntries(entries)),
WRAPPERS: this.propOfPlugins('wrapper'),
})
)

touch(
paths.indexJs,
js({
BEFORE_RENDERS: this.propOfPlugins('beforeRender'),
AFTER_RENDERS: this.propOfPlugins('afterRender'),
})
)
}

public async createCompiler(entries: Entry[]) {
del.sync(paths.docz)
this.generateFilesByTemplate(entries)
return await this.compiler(this.mountConfig(entries))
public async createCompiler() {
return await this.compiler(this.mountConfig())
}

public async createServer(compiler: any): Promise<S> {
Expand Down
84 changes: 78 additions & 6 deletions packages/docz-core/src/Entries.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import * as glob from 'fast-glob'
import * as t from 'babel-types'
import * as fs from 'fs'
import * as path from 'path'
import * as mkdir from 'mkdirp'
import * as prettier from 'prettier'
import { NodePath } from 'babel-traverse'
import { compile } from 'art-template'

import * as paths from './config/paths'

import { traverseAndAssign } from './utils/traverse'
import { Entry, convertToAst } from './Entry'
import { Plugin, IPluginFactory } from './Plugin'

const hasImport = (path: NodePath<any>): boolean =>
path.isImportDeclaration() &&
Expand All @@ -25,18 +33,82 @@ const checkImport = traverseAndAssign<NodePath<t.Node>, boolean>(

const isFile = (entry: string) => checkImport(convertToAst(entry))

const mkd = (dir: string): void => {
try {
fs.lstatSync(dir)
} catch (err) {
mkdir.sync(dir)
}
}

const format = (raw: string) =>
prettier.format(raw, {
semi: false,
singleQuote: true,
trailingComma: 'all',
})

const touch = (file: string, raw: string) => {
const content = /js/.test(path.extname(file)) ? format(raw) : raw

mkd(paths.docz)
fs.writeFileSync(file, content, 'utf-8')
}

const compiled = (file: string) =>
compile(fs.readFileSync(path.join(paths.templates, file), 'utf-8'))

const propOf = (arr: any[], method: keyof IPluginFactory) =>
arr && arr.map(p => p[method]).filter(m => m)

const app = compiled('app.tpl.js')
const js = compiled('index.tpl.js')
const html = compiled('index.tpl.html')

export interface IGenerateFilesParams {
entries: Entry[]
plugins: Plugin[]
theme: string
}

export class Entries {
private files: string[]
public files: string[]
public all: Entry[]

constructor(pattern: string) {
constructor(pattern: string, src: string) {
const ignoreGlob = '!node_modules'

this.files = glob.sync(
const files: string[] = glob.sync(
Array.isArray(pattern) ? [...pattern, ignoreGlob] : [pattern, ignoreGlob]
)

this.files = files
this.all = files.filter(isFile).map(file => new Entry({ file, src }))
}

public map() {
return this.all.reduce((obj: any, entry: Entry) => {
return Object.assign({}, obj, { [entry.filepath]: entry.name })
}, {})
}

public parse(src: string): Entry[] {
return this.files.filter(isFile).map(file => new Entry({ file, src }))
static generateFiles({ entries, theme, plugins }: IGenerateFilesParams) {
touch(paths.indexHtml, html({}))

touch(
paths.appJs,
app({
THEME: theme,
ENTRIES: entries,
WRAPPERS: propOf(plugins, 'wrapper'),
})
)

touch(
paths.indexJs,
js({
BEFORE_RENDERS: propOf(plugins, 'beforeRender'),
AFTER_RENDERS: propOf(plugins, 'afterRender'),
})
)
}
}
12 changes: 6 additions & 6 deletions packages/docz-core/src/Entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ export interface IEntryConstructor {
export class Entry {
public name: string
public filepath: string
public route: string

constructor({ src, file }: IEntryConstructor) {
const ast = convertToAst(file)
const name = getNameFromDoc(ast) || ''
const srcPath = path.resolve(paths.root, src)
const filepath = path.relative(path.relative(paths.root, src), file)
const dir = path.relative(srcPath, path.parse(file).dir)
const route = path.join('/', dir, name)
const filepath = path.relative(paths.root, file)

this.name = name
this.route = route
this.filepath = filepath
}

static parseName(file: string) {
const ast = convertToAst(file)
return getNameFromDoc(ast)
}
}
84 changes: 57 additions & 27 deletions packages/docz-core/src/Server.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { load } from 'load-cfg'
import { FSWatcher } from 'chokidar'
import * as chokidar from 'chokidar'
import del from 'del'

import * as paths from './config/paths'
import { pick } from './utils/helpers'

import { Entry } from './Entry'
import { Entries } from './Entries'
import { Bundler } from './Bundler'
import { Plugin } from './Plugin'

process.env['BABEL_ENV'] = process.env['BABEL_ENV'] || 'development'
process.env['NODE_ENV'] = process.env['NODE_ENV'] || 'development'
process.env.BABEL_ENV = process.env.BABEL_ENV || 'development'
process.env.NODE_ENV = process.env.NODE_ENV || 'development'

const ENV = process.env['NODE_ENV']
const ENV = process.env.NODE_ENV
const HOST = process.env.HOST || '0.0.0.0'
const PROTOCOL = process.env.HTTPS === 'true' ? 'https' : 'http'

Expand All @@ -31,38 +35,33 @@ export interface ConfigArgs extends IServerConstructor {
}

export class Server {
private port: number
private src: string
private plugins: Plugin[]
private entries: Entries
readonly config: ConfigArgs
readonly watcher: FSWatcher
private bundler: Bundler

constructor(args: IServerConstructor) {
const initialArgs = this.getInitialArgs(args)
const { port, theme, files, bundler, src, plugins } = load(
'docz',
initialArgs
)

this.port = port
this.src = src
this.plugins = plugins
this.entries = new Entries(files)

this.bundler = this.getBundler(bundler).bundler({
port,
const config = load('docz', initialArgs)

this.config = config
this.watcher = chokidar.watch(config.files, {
ignored: /(^|[\/\\])\../,
})

this.bundler = this.getBundler(config.bundler).bundler({
...config,
paths,
theme,
src,
plugins,
env: ENV,
host: HOST,
protocol: PROTOCOL,
})
}

private getInitialArgs(args: IServerConstructor) {
return pick(['port', 'theme', 'files', 'bundler', 'src'], args)
return {
...pick(['port', 'theme', 'files', 'bundler', 'src'], args),
plugins: [],
}
}

private getBundler(bundler: string) {
Expand All @@ -73,11 +72,42 @@ export class Server {
}
}

private processEntries(config: ConfigArgs) {
const { files, src, theme, plugins } = config
const cache = new Map()

const generateFilesAndUpdateCache = (entries: Entries) => {
cache.set('map', entries.map())
Entries.generateFiles({ entries: entries.all, plugins, theme })
}

const updateEntries = () =>
generateFilesAndUpdateCache(new Entries(files, src))

const parseToUpdate = (path: string) => {
const name = Entry.parseName(path)
const entry = cache.get('map')[path]
const newEntries = new Entries(files, src)

if (name && name !== entry && newEntries.files.includes(path)) {
generateFilesAndUpdateCache(newEntries)
}
}

this.watcher.on('unlink', updateEntries)
this.watcher.on('change', parseToUpdate)

generateFilesAndUpdateCache(new Entries(files, src))
}

public async start() {
const { bundler, entries, plugins } = this
const { plugins, port } = this.config

del.sync(paths.docz)
this.processEntries(this.config)

const compiler = await bundler.createCompiler(entries.parse(this.src))
const server = await bundler.createServer(compiler)
const compiler = await this.bundler.createCompiler()
const server = await this.bundler.createServer(compiler)

if (plugins && plugins.length > 0) {
for (const plugin of plugins) {
Expand All @@ -86,6 +116,6 @@ export class Server {
}
}

server.listen(this.port)
server.listen(port)
}
}

0 comments on commit 2f073d5

Please sign in to comment.