Skip to content

Instantly share code, notes, and snippets.

@brandonmp
Last active August 16, 2016 13:06
Show Gist options
  • Save brandonmp/ac6670826146a0dfb876955260841a1d to your computer and use it in GitHub Desktop.
Save brandonmp/ac6670826146a0dfb876955260841a1d to your computer and use it in GitHub Desktop.
import Koa from 'koa'
import convert from 'koa-convert'
import webpack from 'webpack'
import webpackConfig from '../build/webpack.config'
import historyApiFallback from 'koa-connect-history-api-fallback'
import serve from 'koa-static'
import proxy from 'koa-proxy'
import _debug from 'debug'
import config from '../config'
import webpackDevMiddleware from './middleware/webpack-dev'
import webpackHMRMiddleware from './middleware/webpack-hmr'
// webpack-dashboard
var Dashboard = require('webpack-dashboard');
var DashboardPlugin = require('webpack-dashboard/plugin');
const debug = _debug('app:server')
const paths = config.utils_paths
const app = new Koa()
// Enable koa-proxy if it has been enabled in the config.
if (config.proxy && config.proxy.enabled) {
app.use(convert(proxy(config.proxy.options)))
}
// This rewrites all routes requests to the root /index.html file
// (ignoring file requests). If you want to implement isomorphic
// rendering, you'll want to remove this middleware.
app.use(convert(historyApiFallback({
verbose: false
})))
// ------------------------------------
// Apply Webpack HMR Middleware
// ------------------------------------
if (config.env === 'development') {
const compiler = webpack(webpackConfig)
// webpack-dashboard
var dashboard = new Dashboard();
compiler.apply(new DashboardPlugin(dashboard.setData));
// Enable webpack-dev and webpack-hot middleware
const { publicPath } = webpackConfig.output
app.use(webpackDevMiddleware(compiler, { quiet: true, publicPath }))
app.use(webpackHMRMiddleware(compiler, { log: () => { } }))
// Serve static assets from ~/src/static since Webpack is unaware of
// these files. This middleware doesn't need to be enabled outside
// of development since this directory will be copied into ~/dist
// when the application is compiled.
app.use(convert(serve(paths.client('static'))))
} else {
debug(
'Server is being run outside of live development mode, meaning it will ' +
'only serve the compiled application bundle in ~/dist. Generally you ' +
'do not need an application server for this and can instead use a web ' +
'server such as nginx to serve your static files. See the "deployment" ' +
'section in the README for more information on deployment strategies.'
)
// Serving ~/dist by default. Ideally these files should be served by
// the web server and not the app server, but this helps to demo the
// server in production.
app.use(convert(serve(paths.dist())))
}
export default app
import webpack from 'webpack'
import _debug from 'debug'
import config from '../config'
const debug = _debug('app:build:webpack-compiler')
const DEFAULT_STATS_FORMAT = config.compiler_stats
export default function webpackCompiler (webpackConfig, statsFormat = DEFAULT_STATS_FORMAT) {
return new Promise((resolve, reject) => {
const compiler = webpack(webpackConfig)
compiler.run((err, stats) => {
const jsonStats = stats.toJson()
debug('Webpack compile completed.')
debug(stats.toString(statsFormat))
if (err) {
debug('Webpack compiler encountered a fatal error.', err)
return reject(err)
} else if (jsonStats.errors.length > 0) {
debug('Webpack compiler encountered errors.')
debug(jsonStats.errors.join('\n'))
return reject(new Error('Webpack compiler encountered errors'))
} else if (jsonStats.warnings.length > 0) {
debug('Webpack compiler encountered warnings.')
debug(jsonStats.warnings.join('\n'))
} else {
debug('No errors or warnings encountered.')
}
resolve(jsonStats)
})
})
}
import WebpackDevMiddleware from 'webpack-dev-middleware'
import applyExpressMiddleware from '../lib/apply-express-middleware'
import _debug from 'debug'
import config from '../../config'
const paths = config.utils_paths
const debug = _debug('app:server:webpack-dev')
export default function (compiler, publicPath) {
debug('Enable webpack dev middleware.')
const middleware = WebpackDevMiddleware(compiler, {
publicPath,
contentBase: paths.client(),
hot: true,
quiet: config.compiler_quiet,
noInfo: config.compiler_quiet,
lazy: false,
stats: config.compiler_stats
})
return async function koaWebpackDevMiddleware (ctx, next) {
let hasNext = await applyExpressMiddleware(middleware, ctx.req, {
end: (content) => (ctx.body = content),
setHeader: function () {
ctx.set.apply(ctx, arguments)
}
})
if (hasNext) {
await next()
}
}
}
import WebpackHotMiddleware from 'webpack-hot-middleware'
import applyExpressMiddleware from '../lib/apply-express-middleware'
import _debug from 'debug'
const debug = _debug('app:server:webpack-hmr')
export default function (compiler, opts) {
debug('Enable Webpack Hot Module Replacement (HMR).')
const middleware = WebpackHotMiddleware(compiler, opts)
return async function koaWebpackHMR (ctx, next) {
let hasNext = await applyExpressMiddleware(middleware, ctx.req, ctx.res)
if (hasNext && next) {
await next()
}
}
}
import webpack from 'webpack'
import cssnano from 'cssnano'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import config from '../config'
import _debug from 'debug'
const debug = _debug('app:webpack:config')
const paths = config.utils_paths
const {__DEV__, __PROD__, __TEST__} = config.globals
debug('Create configuration.')
const webpackConfig = {
name: 'client',
target: 'web',
devtool: config.compiler_devtool,
resolve: {
root: paths.client(),
extensions: ['', '.js', '.jsx', '.json']
},
module: {}
}
// ------------------------------------
// Entry Points
// ------------------------------------
const APP_ENTRY_PATHS = [
'babel-polyfill',
paths.client('main.js')
]
webpackConfig.entry = {
app: __DEV__
? APP_ENTRY_PATHS.concat(`webpack-hot-middleware/client?path=${config.compiler_public_path}__webpack_hmr`)
: APP_ENTRY_PATHS,
vendor: config.compiler_vendor
}
// ------------------------------------
// Bundle Output
// ------------------------------------
webpackConfig.output = {
filename: `[name].[${config.compiler_hash_type}].js`,
path: paths.dist(),
publicPath: config.compiler_public_path
}
// ------------------------------------
// Plugins
// ------------------------------------
webpackConfig.plugins = [
new webpack.DefinePlugin(config.globals),
new HtmlWebpackPlugin({
template: paths.client('index.html'),
hash: false,
favicon: paths.client('static/favicon.ico'),
filename: 'index.html',
inject: 'body',
minify: {
collapseWhitespace: true
}
})
]
if (__DEV__) {
debug('Enable plugins for live development (HMR, NoErrors).')
webpackConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
)
} else if (__PROD__) {
debug('Enable plugins for production (OccurenceOrder, Dedupe & UglifyJS).')
webpackConfig.plugins.push(
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compress: {
unused: true,
dead_code: true,
warnings: false
}
})
)
}
// Don't split bundles during testing, since we only want import one bundle
if (!__TEST__) {
webpackConfig.plugins.push(
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor']
})
)
}
// ------------------------------------
// Pre-Loaders
// ------------------------------------
/*
[ NOTE ]
We no longer use eslint-loader due to it severely impacting build
times for larger projects. `npm run lint` still exists to aid in
deploy processes (such as with CI), and it's recommended that you
use a linting plugin for your IDE in place of this loader.
If you do wish to continue using the loader, you can uncomment
the code below and run `npm i --save-dev eslint-loader`. This code
will be removed in a future release.
webpackConfig.module.preLoaders = [{
test: /\.(js|jsx)$/,
loader: 'eslint',
exclude: /node_modules/
}]
webpackConfig.eslint = {
configFile: paths.base('.eslintrc'),
emitWarning: __DEV__
}
*/
// ------------------------------------
// Loaders
// ------------------------------------
// JavaScript / JSON
webpackConfig.module.loaders = [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel',
query: {
cacheDirectory: true,
plugins: ['transform-runtime'],
presets: ['es2015', 'react', 'stage-0'],
env: {
production: {
presets: ['react-optimize']
}
}
}
},
{
test: /\.json$/,
loader: 'json'
}]
// ------------------------------------
// Style Loaders
// ------------------------------------
// We use cssnano with the postcss loader, so we tell
// css-loader not to duplicate minimization.
const BASE_CSS_LOADER = 'css?sourceMap&-minimize'
// Add any packge names here whose styles need to be treated as CSS modules.
// These paths will be combined into a single regex.
const PATHS_TO_TREAT_AS_CSS_MODULES = [
// 'react-toolbox', (example)
]
// If config has CSS modules enabled, treat this project's styles as CSS modules.
if (config.compiler_css_modules) {
PATHS_TO_TREAT_AS_CSS_MODULES.push(
paths.client().replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&') // eslint-disable-line
)
}
const isUsingCSSModules = !!PATHS_TO_TREAT_AS_CSS_MODULES.length
const cssModulesRegex = new RegExp(`(${PATHS_TO_TREAT_AS_CSS_MODULES.join('|')})`)
// Loaders for styles that need to be treated as CSS modules.
if (isUsingCSSModules) {
const cssModulesLoader = [
BASE_CSS_LOADER,
'modules',
'importLoaders=1',
'localIdentName=[name]__[local]___[hash:base64:5]'
].join('&')
webpackConfig.module.loaders.push({
test: /\.scss$/,
include: cssModulesRegex,
loaders: [
'style',
cssModulesLoader,
'postcss',
'sass?sourceMap'
]
})
webpackConfig.module.loaders.push({
test: /\.css$/,
include: cssModulesRegex,
loaders: [
'style',
cssModulesLoader,
'postcss'
]
})
}
// Loaders for files that should not be treated as CSS modules.
const excludeCSSModules = isUsingCSSModules ? cssModulesRegex : false
webpackConfig.module.loaders.push({
test: /\.scss$/,
exclude: excludeCSSModules,
loaders: [
'style',
BASE_CSS_LOADER,
'postcss',
'sass?sourceMap'
]
})
webpackConfig.module.loaders.push({
test: /\.css$/,
exclude: excludeCSSModules,
loaders: [
'style',
BASE_CSS_LOADER,
'postcss'
]
})
// ------------------------------------
// Style Configuration
// ------------------------------------
webpackConfig.sassLoader = {
includePaths: paths.client('styles')
}
webpackConfig.postcss = [
cssnano({
autoprefixer: {
add: true,
remove: true,
browsers: ['last 2 versions']
},
discardComments: {
removeAll: true
},
discardUnused: false,
mergeIdents: false,
reduceIdents: false,
safe: true,
sourcemap: true
})
]
// File loaders
/* eslint-disable */
webpackConfig.module.loaders.push(
{ test: /\.woff(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff' },
{ test: /\.woff2(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2' },
{ test: /\.otf(\?.*)?$/, loader: 'file?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype' },
{ test: /\.ttf(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream' },
{ test: /\.eot(\?.*)?$/, loader: 'file?prefix=fonts/&name=[path][name].[ext]' },
{ test: /\.svg(\?.*)?$/, loader: 'url?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml' },
{ test: /\.(png|jpg)$/, loader: 'url?limit=8192' }
)
/* eslint-enable */
// ------------------------------------
// Finalize Configuration
// ------------------------------------
// when we don't know the public path (we know it only when HMR is enabled [in development]) we
// need to use the extractTextPlugin to fix this issue:
// http://stackoverflow.com/questions/34133808/webpack-ots-parsing-error-loading-fonts/34133809#34133809
if (!__DEV__) {
debug('Apply ExtractTextPlugin to CSS loaders.')
webpackConfig.module.loaders.filter((loader) =>
loader.loaders && loader.loaders.find((name) => /css/.test(name.split('?')[0]))
).forEach((loader) => {
const [first, ...rest] = loader.loaders
loader.loader = ExtractTextPlugin.extract(first, rest.join('!'))
Reflect.deleteProperty(loader, 'loaders')
})
webpackConfig.plugins.push(
new ExtractTextPlugin('[name].[contenthash].css', {
allChunks: true
})
)
}
export default webpackConfig
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment