Skip to content
This repository was archived by the owner on Jun 3, 2019. It is now read-only.

Commit e27a7ef

Browse files
Move webpack manifest out from index chunk
Enables index chunk hash to become independent of children chunk hashes. The manifest is inlined with every server rendered route in production mode.
1 parent e081d75 commit e27a7ef

File tree

6 files changed

+78
-19
lines changed

6 files changed

+78
-19
lines changed

config/values.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ const values = {
134134
// containing details of all output files for a bundle?
135135
bundleAssetsFileName: 'assets.json',
136136

137+
// What should we name the manifest generated by chunk-manifest-webpack-plugin
138+
// containing mappings for chunk ids and names?
139+
bundleManifestFileName: 'manifest.json',
140+
137141
// node_modules are not included in any bundles that target "node" as a
138142
// runtime (e.g.. the server bundle) as including them often breaks builds
139143
// due to thinks like require statements containing expressions..

internal/webpack/configFactory.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import nodeExternals from 'webpack-node-externals';
55
import path from 'path';
66
import webpack from 'webpack';
77
import WebpackMd5Hash from 'webpack-md5-hash';
8+
import ChunkManifestWebpackPlugin from 'chunk-manifest-webpack-plugin';
89

910
import { happyPackPlugin } from '../utils';
1011
import { ifElse } from '../../shared/utils/logic';
@@ -219,6 +220,17 @@ export default function webpackConfigFactory(buildOptions) {
219220
// even though 1 or 2 may have only changed.
220221
ifClient(() => new WebpackMd5Hash()),
221222

223+
// Since chunk-manifest-webpack-plugin doesn't work with webpack-dev-server
224+
// https://github.com/soundcloud/chunk-manifest-webpack-plugin/issues/26
225+
// we generate manifest only in production mode.
226+
// Also this optimisation is to prevent better long term caching of
227+
// index chunk which can be compromised in development mode.
228+
ifProdClient(() => new ChunkManifestWebpackPlugin({
229+
filename: config('bundleManifestFileName'),
230+
manifestVariable: 'webpackManifest',
231+
inlineManifest: false,
232+
})),
233+
222234
// These are process.env flags that you can use in your code in order to
223235
// have advanced control over what is included/excluded in your bundles.
224236
// For example you may only want certain parts of your code to be

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"babel-preset-stage-3": "6.24.1",
9797
"babel-template": "6.25.0",
9898
"chokidar": "1.7.0",
99+
"chunk-manifest-webpack-plugin": "^1.1.0",
99100
"css-loader": "0.28.4",
100101
"enzyme": "2.9.1",
101102
"enzyme-to-json": "1.5.1",

server/middleware/reactApplication/ServerHTML.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import serialize from 'serialize-javascript';
1313
import config from '../../../config';
1414
import ifElse from '../../../shared/utils/logic/ifElse';
1515
import removeNil from '../../../shared/utils/arrays/removeNil';
16-
import getClientBundleEntryAssets from './getClientBundleEntryAssets';
16+
import {
17+
getClientBundleEntryAssets,
18+
getClientWebpackManifest,
19+
} from './getClientBundleEntryAssets';
1720

1821
import ClientConfig from '../../../config/components/ClientConfig';
1922
import HTML from '../../../shared/components/HTML';
@@ -27,6 +30,11 @@ function KeyedComponent({ children }) {
2730
// Resolve the assets (js/css) for the client bundle's entry chunk.
2831
const clientEntryAssets = getClientBundleEntryAssets();
2932

33+
// Resolve the webpack manifest. Useful only in production mode.
34+
const clientWebpackManifest = process.env.BUILD_FLAG_IS_DEV === 'false' ?
35+
getClientWebpackManifest() : {};
36+
37+
3038
function stylesheetTag(stylesheetFilePath) {
3139
return (
3240
<link href={stylesheetFilePath} media="screen, projection" rel="stylesheet" type="text/css" />
@@ -47,13 +55,18 @@ function ServerHTML(props) {
4755
<script nonce={nonce} type="text/javascript" dangerouslySetInnerHTML={{ __html: body }} />
4856
);
4957

58+
const webpackManifestScript = `
59+
window.webpackManifest = ${JSON.stringify(clientWebpackManifest)};
60+
`;
61+
5062
const headerElements = removeNil([
5163
...ifElse(helmet)(() => helmet.title.toComponent(), []),
5264
...ifElse(helmet)(() => helmet.base.toComponent(), []),
5365
...ifElse(helmet)(() => helmet.meta.toComponent(), []),
5466
...ifElse(helmet)(() => helmet.link.toComponent(), []),
5567
ifElse(clientEntryAssets && clientEntryAssets.css)(() => stylesheetTag(clientEntryAssets.css)),
5668
...ifElse(helmet)(() => helmet.style.toComponent(), []),
69+
ifElse(process.env.BUILD_FLAG_IS_DEV === 'false')(() => inlineScript(webpackManifestScript)),
5770
]);
5871

5972
const bodyElements = removeNil([

server/middleware/reactApplication/getClientBundleEntryAssets.js

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,21 @@ import { resolve as pathResolve } from 'path';
77
import appRootDir from 'app-root-dir';
88
import config from '../../../config';
99

10-
let resultCache;
10+
function getJSONFromFile(fileName) {
11+
const filePath = pathResolve(
12+
appRootDir.get(),
13+
config('bundles.client.outputPath'),
14+
`./${fileName}`,
15+
);
16+
17+
if (!fs.existsSync(filePath)) {
18+
throw new Error(
19+
`We could not find the "${filePath}" file. Please ensure that the client bundle has been built.`,
20+
);
21+
}
22+
23+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
24+
}
1125

1226
/**
1327
* Retrieves the js/css for the named chunks that belong to our client bundle.
@@ -22,31 +36,33 @@ let resultCache;
2236
* to the render logic. Having this method allows us to easily fetch
2337
* the respective assets simply by using a chunk name. :)
2438
*/
25-
export default function getClientBundleEntryAssets() {
39+
export function getClientBundleEntryAssets() {
40+
let resultCache;
41+
2642
// Return the assets json cache if it exists.
2743
// In development mode we always read the assets json file from disk to avoid
2844
// any cases where an older version gets cached.
2945
if (process.env.BUILD_FLAG_IS_DEV === 'false' && resultCache) {
3046
return resultCache;
3147
}
3248

33-
const assetsFilePath = pathResolve(
34-
appRootDir.get(),
35-
config('bundles.client.outputPath'),
36-
`./${config('bundleAssetsFileName')}`,
37-
);
49+
const clientBundleAssetsJSON = getJSONFromFile(config('bundleAssetsFileName'));
3850

39-
if (!fs.existsSync(assetsFilePath)) {
40-
throw new Error(
41-
`We could not find the "${assetsFilePath}" file, which contains a list of the assets of the client bundle. Please ensure that the client bundle has been built.`,
42-
);
51+
if (typeof clientBundleAssetsJSON.index === 'undefined') {
52+
throw new Error('No asset data found for expected "index" entry chunk of client bundle.');
4353
}
4454

45-
const readAssetsJSONFile = () => JSON.parse(fs.readFileSync(assetsFilePath, 'utf8'));
46-
const assetsJSONCache = readAssetsJSONFile();
47-
if (typeof assetsJSONCache.index === 'undefined') {
48-
throw new Error('No asset data found for expected "index" entry chunk of client bundle.');
55+
resultCache = clientBundleAssetsJSON.index;
56+
return resultCache;
57+
}
58+
59+
export function getClientWebpackManifest() {
60+
let resultCache;
61+
62+
if (resultCache) {
63+
return resultCache;
4964
}
50-
resultCache = assetsJSONCache.index;
65+
66+
resultCache = getJSONFromFile(config('bundleManifestFileName'));
5167
return resultCache;
5268
}

yarn.lock

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,12 @@ chokidar@1.7.0, chokidar@^1.4.3, chokidar@^1.6.1:
12521252
optionalDependencies:
12531253
fsevents "^1.0.0"
12541254

1255+
chunk-manifest-webpack-plugin@^1.1.0:
1256+
version "1.1.0"
1257+
resolved "https://registry.yarnpkg.com/chunk-manifest-webpack-plugin/-/chunk-manifest-webpack-plugin-1.1.0.tgz#21e73e5d31ed09e15f610e41385a276224d39af2"
1258+
dependencies:
1259+
webpack-core "^0.6.9"
1260+
12551261
ci-info@^1.0.0:
12561262
version "1.0.0"
12571263
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534"
@@ -5722,7 +5728,7 @@ sort-keys@^1.0.0:
57225728
dependencies:
57235729
is-plain-obj "^1.0.0"
57245730

5725-
source-list-map@^0.1.7:
5731+
source-list-map@^0.1.7, source-list-map@~0.1.7:
57265732
version "0.1.8"
57275733
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
57285734

@@ -5744,7 +5750,7 @@ source-map@0.5.6, source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source
57445750
version "0.5.6"
57455751
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
57465752

5747-
source-map@^0.4.4:
5753+
source-map@^0.4.4, source-map@~0.4.1:
57485754
version "0.4.4"
57495755
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
57505756
dependencies:
@@ -6261,6 +6267,13 @@ webpack-bundle-analyzer@2.8.2:
62616267
opener "^1.4.3"
62626268
ws "^2.3.1"
62636269

6270+
webpack-core@^0.6.9:
6271+
version "0.6.9"
6272+
resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2"
6273+
dependencies:
6274+
source-list-map "~0.1.7"
6275+
source-map "~0.4.1"
6276+
62646277
webpack-dev-middleware@1.11.0:
62656278
version "1.11.0"
62666279
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz#09691d0973a30ad1f82ac73a12e2087f0a4754f9"

0 commit comments

Comments
 (0)