-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Build: Generate stable module and chunk IDs and a better chunk hash #11867
Conversation
6d7a988
to
3a7f27f
Compare
To test:
|
return chunkIds; | ||
} | ||
|
||
function collectChunkIds( chunk ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop this? not needed.
I added a bunch more explanation to the summary of the PR that should help with testing (and I hope understanding). |
0e0278f
to
2aa04d7
Compare
2aa04d7
to
d4582bb
Compare
…generators. The module ID generator is lifting mostly from webpack-stable-module-id-and-hash. It works quite well, and could be replaced with the new HashedModuleIdPlugin when we move to webpack 2. The chunk ID generator is based on it, but uses the chunk name instead of the module path as a key into the chunk ID. The chunk hash generator is based on webpack-chunk-hash, with the addition of the dependant chunks in the hash content. This breaks the hash when the chunk ids of dependent chunks change. With the stable chunk module this shouldn't happen, but in case we drop it, add it for safety.
…hasher and the the id generators
d4582bb
to
ac9f8a2
Compare
Rebased on current |
|
And, as per @blowery's request, https://gist.github.com/ockham/e5175929d87194b3779aba02457facbd |
Tested this out locally in a docker build. All LGTM 👍. |
server/bundler/plugin.js
Outdated
@@ -29,11 +29,11 @@ ChunkFileNames.prototype.apply = function( compiler ) { | |||
this.indent( [ | |||
"script.onerror = script.onload = script.onreadystatechange = null;", | |||
"delete installedChunks[ chunkId ];", | |||
"window.__chunkErrors[ " + JSON.stringify( chunkMaps.name ) + "[chunkId]||chunkId ]=new Error();", | |||
"window.__chunkErrors[ " + JSON.stringify( chunkMaps.name, null, ' ' ) + "[chunkId]||chunkId ]=new Error();", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does the null do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the null
is the custom formatter. you have to pass it to get at the third arg, which is the string to use when pretty printing. Dropped it all and went back to plain JSON printing. This was really just for debugging. Makes the JSON easier to read.
webpack-dll.config.js
Outdated
@@ -3,6 +3,7 @@ | |||
*/ | |||
const path = require( 'path' ); | |||
const webpack = require( 'webpack' ); | |||
const WebpackStableModuleId = require( './server/bundler/webpack/stable-module-id' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't even need this file now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dropped
server/bundler/plugin.js
Outdated
@@ -18,7 +18,7 @@ ChunkFileNames.prototype.apply = function( compiler ) { | |||
"// start chunk loading", | |||
"installedChunks[ chunkId ] = [ callback ];", | |||
"window.__chunkErrors = window.__chunkErrors || {};", | |||
"window.__chunkErrors[ " + JSON.stringify( chunkMaps.name ) + "[chunkId]||chunkId ]=null;", | |||
"window.__chunkErrors[ " + JSON.stringify( chunkMaps.name, null, ' ' ) + "[chunkId]||chunkId ]=null;", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix these to not use spaces for formatting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
The goal of the PR is to make chunk hashes, chunk IDs and module IDs stable over time. Stable chunk hashes require stable module IDs and stable chunks IDs. Webpack uses monotonically increasing IDs by default; this results in instability at multiple levels.
Module IDs
react
may map to module ID4
in one build and then map to module ID5
in the next. Modules that importreact
would then change, even if they themselves did not. This, in turn, should make the chunk hash change when it should not.This PR moves us to a stable module name -> module ID mapping, based on the md5 of the module path. This makes module IDs stable across builds.
A couple folks asked if we could we use the
NamedModulesPlugin
instead. We definitely could, though the Webpack folks are now recommending the HashedModulesPlugin for production, which is what this replicates.Chunk IDs
As we add more and more chunks with
asyncRequire
, we add more and more chunks to the mix. Chunk IDs, like Module IDs, are just an increasing ID by default. This means that Chunk A may map to ID3
in one build and chunk5
in the next. This means that other chunks that depend on Chunk A would need to update their hash, even if nothing within the chunk changes.This PR moves us to a stable chunk name -> chunk ID mapping, based on the chunk's
name
property. It requires named chunks, which all of our chunks are, oncecommons
is removed.Now when we introduce new chunks, those chunks get a new unique ID and references remain stable
Chunk Hashes
The default webpack chunk hash generator is pretty complicated and depends on things that are no longer necessary when we have stable module IDs and stable chunk IDs.
This PR adds a new chunk hasher that uses an MD5 hash of the content plus the hash seed to generate the hash.
Further Explanation
The module ID generator is lifting mostly from
webpack-stable-module-id-and-hash
. It works quite well, and could be replaced with the newHashedModuleIdPlugin
when we move towebpack@2
. There's no way to turn off the chunk hash generator inwebpack-stable-module-id-and-hash
though, so I just pulled it. Plus we have control if we need to modify it.The chunk ID generator is based on it, but uses the chunk name instead of
the module path as a key into the chunk ID.
The chunk hash generator is based on
webpack-chunk-hash
. We could probably just usewebpack-chunk-hash
directly, but having the code locally will likely be useful.This also moves the vendor dll into a commons chunk. Otherwise we can't control the module IDs generated by the DLL Reference plugin. The module IDs within the vendor DLL were fine, but the DllRefererncePlugin aliases them to a monotonically increasing ID which is not stable across builds. We need all the module and chunk IDs to be stable to make this all work.
I also removed the commons chunk since it was now down to 4k zipped and only really needed by a few sections. This saves 4k for a bunch of section loads.
Testing
To test this, first make a base build with
CALYPSO_ENV=production make build
. Copy offpublic
to a safe place (cp -r public base-public
). Now start fiddling with things in the source tree and npm dependencies and see how the built code changes (diff -rqu base-public public
).Build calypso off the commit prior to this one (not current master). Compare to this PR. Do the hashes for all of the chunks change? They should.
When changing something in
vendor
, onlyvendor
should change. Try updatingreact
ormoment
orreact-redux
. Expect to see a new vendor chunk and no other changes.When something in an asyncRequire chunk changes, only that chunk should change. Try modifying code in
reader/sidebar/expandable.jsx
. What chunks get new hashes? It should be limited toasync-load-reader-sidebar
.When you add a new module to the build chunk, only the build chunk should change. Try importing something new from
client/boot
. What changes? It should just be the build chunk. The new module should get a new unique ID and other module IDs should not be affected.