Skip to content

Commit

Permalink
feat(docz-core): split bundler logic
Browse files Browse the repository at this point in the history
  • Loading branch information
pedronauck committed Apr 15, 2018
1 parent 7484267 commit a7db904
Show file tree
Hide file tree
Showing 16 changed files with 330 additions and 287 deletions.
67 changes: 67 additions & 0 deletions packages/playgrodd-core/src/Bundler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import * as fs from 'fs'
import * as mkdir from 'mkdirp'
import * as del from 'del'
import { compile } from 'art-template'

import * as paths from './config/paths'
import { Entry } from './Entry'

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

const touch = (file: string, content: string) => {
mkd(paths.PLAYGRODD)
fs.writeFileSync(file, content, 'utf-8')
}

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

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

interface IConstructorParams<C, S> {
id: string
config: TConfigFn<C>
setup: TSetupFn<C>
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
private config: TConfigFn<C>
private setup: TSetupFn<C>
private server: TServerFn<S>

constructor({ id, config, setup, server }: IConstructorParams<C, S>) {
this.id = id
this.config = config
this.setup = setup
this.server = server
}

public async createCompiler(entries: Entry[]) {
const config = this.config(entries)

await del(paths.PLAYGRODD)
touch(paths.APP_JS, app({ entries }))
touch(paths.INDEX_JS, js({}))
touch(paths.INDEX_HTML, html({}))

return await this.setup(config)
}

public async createServer(compiler: any): Promise<S> {
return await this.server(compiler)
}
}
43 changes: 43 additions & 0 deletions packages/playgrodd-core/src/Entries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as glob from 'fast-glob'
import * as t from 'babel-types'
import { NodePath } from 'babel-traverse'

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

const hasPlaygroddImported = (path: NodePath<any>): boolean =>
path.isImportDeclaration() &&
path.node &&
path.node.source &&
path.node.source.value === 'playgrodd'

const hasDocFn = (path: NodePath<any>): boolean =>
path.node.specifiers &&
path.node.specifiers.some(
(node: NodePath<any>) =>
t.isImportSpecifier(node) && node.imported.name === 'doc'
)

const checkIfImportPlaygrodd = traverseAndAssign<NodePath<t.Node>, boolean>(
path => hasPlaygroddImported(path) && hasDocFn(path),
path => true
)

const isPlaygroddFile = (entry: string) =>
checkIfImportPlaygrodd(convertToAst(entry))

export class Entries {
private files: string[]

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

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

public parse(): Entry[] {
return this.files.filter(isPlaygroddFile).map(file => new Entry(file))
}
}
35 changes: 35 additions & 0 deletions packages/playgrodd-core/src/Entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as fs from 'fs'
import * as path from 'path'
import * as t from 'babel-types'
import { parse } from 'babylon'

import * as paths from './config/paths'
import { traverseAndAssign } from './utils/traverse'

export const convertToAst = (entry: string): t.File =>
parse(fs.readFileSync(entry, 'utf-8'), {
sourceType: 'module',
plugins: ['jsx'],
})

const getNameFromDoc = traverseAndAssign<any, string>(
path => path.isCallExpression() && path.node.callee.name === 'doc',
path => path.node.arguments[0].value
)

export class Entry {
public name: string
public filepath: string
public route: string

constructor(file: string) {
const ast = convertToAst(file)
const name = getNameFromDoc(ast) || ''
const route = path.join('/', path.parse(file).dir, name)
const filepath = path.relative(paths.ROOT, file)

this.name = name
this.route = route
this.filepath = filepath
}
}
1 change: 1 addition & 0 deletions packages/playgrodd-core/src/bundlers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { bundler as webpack } from './webpack'
142 changes: 142 additions & 0 deletions packages/playgrodd-core/src/bundlers/webpack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import * as errorOverlayMiddleware from 'react-dev-utils/errorOverlayMiddleware'
import * as path from 'path'
import { Application } from 'express'
import { Loader, Configuration } from 'webpack'
import * as webpack from 'webpack'
import * as merge from 'deepmerge'
import * as HtmlWebpackPlugin from 'html-webpack-plugin'
import * as webpackDevServerUtils from 'react-dev-utils/WebpackDevServerUtils'
import * as WebpackDevServer from 'webpack-dev-server'

import { loadConfig } from '../utils/load-config'
import * as paths from '../config/paths'

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

export const PORT = 3000
export const PROTOCOL = process.env.HTTPS === 'true' ? 'https' : 'http'
export const HOST = process.env.HOST || '0.0.0.0'

const devServerConfig = () => ({
compress: true,
clientLogLevel: 'none',
contentBase: paths.PLAYGRODD,
watchContentBase: true,
hot: true,
quiet: true,
noInfo: true,
publicPath: '/',
https: PROTOCOL === 'https',
host: HOST,
overlay: false,
watchOptions: {
ignored: /node_modules/,
},
stats: {
colors: true,
chunks: false,
chunkModules: false,
},
historyApiFallback: {
disableDotRule: true,
},
before(app: Application) {
app.use(errorOverlayMiddleware())
},
})

const babelLoader = (): Loader => {
const babelrc = loadConfig('babel', null)
const options = merge(babelrc, {
babelrc: false,
cacheDirectory: true,
presets: [
require.resolve('@babel/preset-env'),
require.resolve('@babel/preset-react'),
],
plugins: [require.resolve('react-hot-loader/babel')],
})

return {
options,
loader: require.resolve('babel-loader'),
}
}

const config = (entries: Entry[]): Configuration => ({
mode: 'development',
context: paths.ROOT,
devtool: '#source-map',
entry: [
require.resolve('babel-polyfill'),
require.resolve('react-dev-utils/webpackHotDevClient'),
paths.INDEX_JS,
],
output: {
pathinfo: true,
path: paths.DIST,
publicPath: '/',
filename: 'static/js/[name].js',
sourceMapFilename: 'static/js/[name].js.map',
crossOriginLoading: 'anonymous',
devtoolModuleFilenameTemplate: (info: any) =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: [paths.ROOT],
use: babelLoader(),
},
],
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
modules: [paths.ROOT, 'node_modules'],
alias: {
'@babel/runtime': path.dirname(
require.resolve('@babel/runtime/package.json')
),
},
},
devServer: {
logLevel: 'silent',
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new HtmlWebpackPlugin({
inject: true,
template: paths.INDEX_HTML,
}),
],
})

const setup = (config: Configuration) => {
const appName = require(paths.PACKAGE_JSON).name
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'
const urls = webpackDevServerUtils.prepareUrls(protocol, HOST, PORT)

return webpackDevServerUtils.createCompiler(
webpack,
config,
appName,
urls,
true
)
}

const server = (compiler: any): WebpackDevServer =>
new WebpackDevServer(compiler, devServerConfig())

export const bundler = (): Bundler =>
new Bundler<Configuration, WebpackDevServer>({
id: 'webpack',
config,
setup,
server,
})
48 changes: 0 additions & 48 deletions packages/playgrodd-core/src/compiler/create-compiler.ts

This file was deleted.

Loading

0 comments on commit a7db904

Please sign in to comment.