import fs from 'fs' import invariant from 'invariant' import webpack from 'webpack' const createBundle = (webpackStats) => { const { publicPath, assetsByChunkName } = webpackStats const createURL = (asset) => publicPath + asset const getAssets = (chunks = [ 'main' ]) => (Array.isArray(chunks) ? chunks : [ chunks ]).reduce((memo, chunk) => ( memo.concat(assetsByChunkName[chunk] || []) ), []) const getScriptAssets = (...args) => getAssets(...args) .filter(asset => (/\.js$/).test(asset)) .map(createURL) const getStyleAssets = (...args) => getAssets(...args) .filter(asset => (/\.css$/).test(asset)) .map(createURL) return { createURL, getAssets, getScriptAssets, getStyleAssets } } /** * An express middleware that sets req.manifest from the build manifest * in the given file. Should be used in production to get consistent hashes. */ export const assetsManifest = (webpackManifestFile) => { let manifest try { manifest = JSON.parse(fs.readFileSync(webpackManifestFile, 'utf8')) } catch (error) { invariant( false, 'assetsManifest middleware cannot read the manifest file "%s"; ' + 'do `npm run build` before starting the server', webpackManifestFile ) } return (req, res, next) => { req.manifest = manifest next() } } /** * An express middleware that sets req.bundle from the build * info in the given stats file. Should be used in production. */ export const staticAssets = (webpackStatsFile) => { let stats try { stats = JSON.parse(fs.readFileSync(webpackStatsFile, 'utf8')) } catch (error) { invariant( false, 'staticAssets middleware cannot read the build stats in %s; ' + 'do `npm run build` before starting the server', webpackStatsFile ) } const bundle = createBundle(stats) return (req, res, next) => { req.bundle = bundle next() } } /** * An express middleware that sets req.bundle from the * latest result from a running webpack compiler (i.e. using * webpack-dev-middleware). Should only be used in dev. */ export const devAssets = (webpackCompiler) => { let bundle webpackCompiler.plugin('done', (stats) => { bundle = createBundle(stats.toJson()) }) return (req, res, next) => { invariant( bundle != null, 'devAssets middleware needs a running compiler; ' + 'use webpack-dev-middleware in front of devAssets' ) req.bundle = bundle next() } } /** * Creates a webpack compiler that automatically inlines the * webpack dev runtime in all entry points. */ export const createDevCompiler = (webpackConfig, webpackRuntimeModuleID) => webpack({ ...webpackConfig, entry: prependModuleID( webpackConfig.entry, webpackRuntimeModuleID ) }) /** * Returns a modified copy of the given webpackEntry object with * the moduleID in front of all other assets. */ const prependModuleID = (webpackEntry, moduleID) => { if (typeof webpackEntry === 'string') return [ moduleID, webpackEntry ] if (Array.isArray(webpackEntry)) return [ moduleID, ...webpackEntry ] if (webpackEntry && typeof webpackEntry === 'object') { const entry = { ...webpackEntry } for (const chunkName in entry) if (entry.hasOwnProperty(chunkName)) entry[chunkName] = prependModuleID(entry[chunkName], moduleID) return entry } throw new Error('Invalid webpack entry object') }