diff --git a/package.json b/package.json index f64dc823..ef5813ba 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "is-windows-bash": "1.0.3", "json-loader": "0.5.4", "loader-utils": "1.1.0", + "md5-file": "3.1.1", "minimist": "1.2.0", "mitt": "1.1.0", "mkdirp-then": "1.2.0", diff --git a/server/build/index.js b/server/build/index.js index 0fcff294..e13d36ff 100644 --- a/server/build/index.js +++ b/server/build/index.js @@ -5,14 +5,15 @@ import uuid from 'uuid' import del from 'del' import webpack from './webpack' import replaceCurrentBuild from './replace' +import md5File from 'md5-file/promise' export default async function build (dir) { const buildDir = join(tmpdir(), uuid.v4()) const compiler = await webpack(dir, { buildDir }) try { - const webpackStats = await runCompiler(compiler) - await writeBuildStats(buildDir, webpackStats) + await runCompiler(compiler) + await writeBuildStats(buildDir) await writeBuildId(buildDir) } catch (err) { console.error(`> Failed to build on ${buildDir}`) @@ -44,17 +45,18 @@ function runCompiler (compiler) { }) } -async function writeBuildStats (dir, webpackStats) { - const chunkHashMap = {} - webpackStats.chunks - // We are not interested about pages - .filter(({ files }) => !/^bundles/.test(files[0])) - .forEach(({ hash, files }) => { - chunkHashMap[files[0]] = { hash } - }) - +async function writeBuildStats (dir) { + // Here we can't use hashes in webpack chunks. + // That's because the "app.js" is not tied to a chunk. + // It's created by merging a few assets. (commons.js and main.js) + // So, we need to generate the hash ourself. + const assetHashMap = { + 'app.js': { + hash: await md5File(join(dir, '.next', 'app.js')) + } + } const buildStatsPath = join(dir, '.next', 'build-stats.json') - await fs.writeFile(buildStatsPath, JSON.stringify(chunkHashMap), 'utf8') + await fs.writeFile(buildStatsPath, JSON.stringify(assetHashMap), 'utf8') } async function writeBuildId (dir) { diff --git a/server/build/plugins/combine-assets-plugin.js b/server/build/plugins/combine-assets-plugin.js new file mode 100644 index 00000000..959c14cd --- /dev/null +++ b/server/build/plugins/combine-assets-plugin.js @@ -0,0 +1,26 @@ +// This plugin combines a set of assets into a single asset +// This should be only used with text assets, +// otherwise the result is unpredictable. +export default class CombineAssetsPlugin { + constructor ({ input, output }) { + this.input = input + this.output = output + } + + apply (compiler) { + compiler.plugin('after-compile', (compilation, callback) => { + let newSource = '' + this.input.forEach((name) => { + newSource += `${compilation.assets[name].source()}\n` + delete compilation.assets[name] + }) + + compilation.assets[this.output] = { + source: () => newSource, + size: () => newSource.length + } + + callback() + }) + } +} diff --git a/server/build/webpack.js b/server/build/webpack.js index e4b0403d..0aff98de 100644 --- a/server/build/webpack.js +++ b/server/build/webpack.js @@ -7,6 +7,7 @@ import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin' import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin' import UnlinkFilePlugin from './plugins/unlink-file-plugin' import JsonPagesPlugin from './plugins/json-pages-plugin' +import CombineAssetsPlugin from './plugins/combine-assets-plugin' import getConfig from '../config' import * as babelCore from 'babel-core' import findBabelConfig from './babel/find-config' @@ -119,6 +120,10 @@ export default async function createCompiler (dir, { dev = false, quiet = false, } } else { plugins.push( + new CombineAssetsPlugin({ + input: ['commons.js', 'main.js'], + output: 'app.js' + }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, sourceMap: false diff --git a/server/document.js b/server/document.js index 5bd502af..d169cfe4 100644 --- a/server/document.js +++ b/server/document.js @@ -59,16 +59,36 @@ export class NextScript extends Component { _documentProps: PropTypes.any } - getChunkScript (filename) { + getChunkScript (filename, additionalProps = {}) { const { __NEXT_DATA__ } = this.context._documentProps let { buildStats } = __NEXT_DATA__ const hash = buildStats ? buildStats[filename].hash : '-' return ( -