unpkg/modules/server/AssetsUtils.js

148 lines
3.5 KiB
JavaScript

const fs = require('fs')
const invariant = require('invariant')
const webpack = require('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.
*/
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.
*/
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.
*/
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.
*/
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')
}
module.exports = {
assetsManifest,
staticAssets,
devAssets,
createDevCompiler
}