Skip to content
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

Webpack 4 #4077

Merged
merged 1 commit into from
May 20, 2018
Merged

Webpack 4 #4077

merged 1 commit into from
May 20, 2018

Conversation

andriijas
Copy link
Contributor

@andriijas andriijas commented Feb 26, 2018

  • Update to webpack 4.x.x
  • Utilize webpack 4 development and production modes
  • Upgrade webpack dev server
  • webpack 4 compatible release of thread-loader
  • webpack 4 compatible release of HtmlWebpackPlugin
  • webpack 4 compatible release of SwPrecacheWebpackPlugin
  • webpack 4 compatible release candidate of WebpackManifestPlugin
  • Update README
  • Update WebpackDevServerUtils
  • Update InterpolateHtmlPlugin
  • Update ModuleScopePlugin
  • Update WatchMissingNodeModulesPlugin
  • Use optimization.minimizer for custom uglify options
  • Use optimization.splitChunks for vendor splitting (https://twitter.com/wSokra/status/969633336732905474)
  • create-react-app polyfills included in vendor chunk
  • Use optimization.runtimeChunk for long term caching (https://twitter.com/wSokra/status/969679223278505985)
  • Remove react-error-overlay is not meant for use in production. warning from dev mode by updating webpack config to proxy process.env.NODE_ENV from ancestor
  • Replace ExtractTextPlugin with MiniCssExtractPlugin
  • Implicit webpack.NamedModulesPlugin in dev config as its default in webpack 4
  • Disable webpack performance hints as we have our own filesize reporter
  • Move InterpolateHtmlPlugin to make it tapable on HtmlWebpackPlugin
  • Replace ExtractTextPlugin with MiniCssExtractPlugin
  • Switch to css whole file minification via OptimizeCSSAssetsPlugin rather than per module css minification to gain performance

Fixes: #3878

@@ -18,10 +18,11 @@ class ModuleScopePlugin {

apply(resolver) {
const { appSrc } = this;
resolver.plugin('file', (request, callback) => {
resolver.hooks.file.tap('ModuleScopePlugin', (request, callback) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now you can reference the documentation of tapable. I believe you want to use tapAsync in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @simonkberg! While thinking about it, since the plugin isnt doing any async should be valid to use .tap, return early where possible then just throw the error instead of passing it to the callback? What do you think?

// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: path.resolve('public/index.html'),
}),
// ...
],
// Makes the public URL available as %PUBLIC_URL% in index.html, e.g.:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved below HtmlWebpackPlugin otherwise the tapable hooks are not available for tapping

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is down one line too far outside the closing bracket for plugins:[...]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thephw thanks, fixed!

"uglifyjs-webpack-plugin": "1.1.6",
"sw-precache-webpack-plugin": "goldhand/sw-precache-webpack-plugin#master",
"thread-loader": "1.1.4",
"uglifyjs-webpack-plugin": "1.2.2",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we should just use the built-in uglifyjs now that we use webpack 4 https://github.com/webpack-contrib/uglifyjs-webpack-plugin#uglifyjs-webpack-plugin

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree but the info on the link is obsolete. (Error: webpack.optimize.UglifyJsPlugin has been removed, please use config.optimization.minimize instead.)

I will move it to config.optimization when the webpack 4 doc is released.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see, thanks for the info!

@mnemanja
Copy link

https://medium.com/webpack/webpack-4-released-today-6cdb994702d4 :)

@@ -18,10 +18,11 @@ class ModuleScopePlugin {

apply(resolver) {
const { appSrc } = this;
resolver.plugin('file', (request, callback) => {
resolver.hooks.file.tap('ModuleScopePlugin', (request, callback) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now you can reference the documentation of tapable. I believe you want to use tapAsync in this case.

@pmdarrow
Copy link

@andriijas it looks like you're upgrading a lot of dependencies in this PR unrelated to Webpack, what's the reason for that?

@pmdarrow
Copy link

pmdarrow commented Feb 28, 2018 via email

@langpavel
Copy link

langpavel commented Feb 28, 2018

Maybe we should have one config now that we are using webpack 4 mode option: #4030

Yes, definitely :-) Now, I have app which should support SSR (Server Side Rendering).
This should not be a big deal, but it is now and it will be for future bees 🐝 too.

In addition I propose to think forward.. You for example can create one webpack.config.client.js and prepare configuration to be easily extensible to multi-target after eject:

Example of my current webpack.config.js:
const getEnvironment = require('./env');
const rawEnv = getEnvironment().raw;

const isDevelopment = rawEnv.NODE_ENV === 'development';
const isProduction = rawEnv.NODE_ENV === 'production';

if (!(isDevelopment || isProduction)) {
  throw new Error(
    'The NODE_ENV environment variable must be "development" or "production".'
  );
}

// https://webpack.js.org/configuration/configuration-types/#exporting-multiple-configurations
module.exports = [
  (isProduction
    ? require('./webpack.config.client.prod')
    : require('./webpack.config.client.dev')
  ),
  (isProduction
    ? require('./webpack.config.server.prod')
    : require('./webpack.config.server.dev')
  ),
];

Thanks for the hard work! ❤️

@andriijas
Copy link
Contributor Author

update

  • now using new release of html-webpack-plugin
  • working with maintainers of precache and manifest plugins to get releases out

@pmdarrow actually i updated to try to pin point which dependency that spits out

(node:79963) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
(node:79963) DeprecationWarning: Tapable.apply is deprecated. Call apply on the plugin directly instead

but i havent managed to figure out which dependency still uses the old webpack plugin api.

@langpavel Looking at the configs in CRA i think there are equally many differences as similarities. Merging the files, there will be a lot of branching/if statements checking which env we are targeting.
I read somewhere that with webpack 5 released later this year extract text plugin will be deprecated,from what I can see by comparing dev and prod configs it will be easier to merge them then. But hey, if you really want it, you can pop a PR when webpack 4 gets in :)

@miraage
Copy link

miraage commented Mar 1, 2018

optimize.splitChuks should be configurable via .env files if you ask me.

@AubreyHewes
Copy link

AubreyHewes commented Mar 2, 2018

nice job @andriijas 🥇

FYI from my mirror of your analysis (things I figured out before I found your PR);

DeprecationWarning: Tapable.plugin is deprecated. Use new API on .hooks instead
DeprecationWarning: Tapable.apply is deprecated. Call apply on the plugin directly

both warnings seem to stem from html-webpack-plugin

The warnings are emitted via package tapable; the webpack compiler is an extension of Tapable
https://github.com/webpack/webpack/blob/v4.0.1/lib/webpack.js#L37

Reproducible with at least one plugin html-webpack-plugin (3.0.3 sic 3.0.2)

`node --trace-deprecation scripts/build.js `
Creating an optimized production build...
(node:4413) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
    at HtmlWebpackPlugin.apply (/app/node_modules/html-webpack-plugin/index.js:65:14)
    at webpack (/app/node_modules/webpack/lib/webpack.js:37:12)
    at build (/app/client/scripts/build.js:46:20)
    at measureFileSizesBeforeBuild.then (/app/client/scripts/build.js:101:12)
    at <anonymous>
(node:4413) DeprecationWarning: Tapable.apply is deprecated. Call apply on the plugin directly instead
    at Object.compileTemplate (/app/node_modules/html-webpack-plugin/lib/compiler.js:47:17)
    at compiler.plugin (/app/node_modules/html-webpack-plugin/index.js:67:42)
    at AsyncParallelHook.eval [as callAsync] (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:13:1)
    at AsyncParallelHook.lazyCompileHook [as _callAsync] (/app/node_modules/tapable/lib/Hook.js:35:21)
    at hooks.beforeCompile.callAsync.err (/app/node_modules/webpack/lib/Compiler.js:459:20)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (/app/node_modules/tapable/lib/Hook.js:35:21)
    at Compiler.compile (/app/node_modules/webpack/lib/Compiler.js:452:28)
    at readRecords.err (/app/node_modules/webpack/lib/Compiler.js:200:11)
    at Compiler.readRecords (/app/node_modules/webpack/lib/Compiler.js:322:11)
    at hooks.run.callAsync.err (/app/node_modules/webpack/lib/Compiler.js:197:10)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (/app/node_modules/tapable/lib/Hook.js:35:21)
    at hooks.beforeRun.callAsync.err (/app/node_modules/webpack/lib/Compiler.js:194:19)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (/app/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:15:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (/app/node_modules/tapable/lib/Hook.js:35:21)
Compiled successfully.

my 2 pennies

@andriijas
Copy link
Contributor Author

@AubreyHewes you are right, while html-webpack-plugin added support for the new webpack plugin api they didnt remove/upgrade all deprecated calls to compiler.plugin.

@thephw
Copy link

thephw commented Mar 4, 2018

FWIW, the associated issue on html-webpack-plugin is already open, digging through the same issues myself at the moment.

jantimon/html-webpack-plugin#874

@andriijas
Copy link
Contributor Author

andriijas commented Mar 6, 2018

Update 6/3/18

  • PR approaching ready status
  • All blockers except external dependencies resolved (precache plugin author promised release later this week or next week, PR pending with manifest, extract text plugin in beta status)
  • Tested on medium size SPA locally, works great, about same compile time as with webpack 3 though :( but total bundle size shrunk from 1.6 to 1.4mb :)

@TheLarkInn
Copy link

TheLarkInn commented May 12, 2018

@andriijas let me see if I can help out :) I've cloned your branch and will see what I find.

@TheLarkInn
Copy link

@andriijas I may have done merge commit wrong PR'ing into your branch, but let's see how this turns out. andriijas#2

@TheLarkInn
Copy link

This is just resolving conflicts and running CI again. Is there any way to run kitchen sink tests locally?

@andriijas
Copy link
Contributor Author

Thx @TheLarkInn - I'm spending the day in the car. It should be possible. Eventually need to install node 8 to reproduce the specific error locally. Not sure. The merge commit might be because I've yet to rebase the latest changes from next. We can resolve that later.

@iansu
Copy link
Contributor

iansu commented May 13, 2018

@TheLarkInn Unfortunately the e2e tests don't work reliably when run locally. They install some global packages and mess with your yarn and npm settings so you probably don't want to run them on your machine. There's a Docker version that someone contributed but that's also broken right now. 😞

@TheLarkInn
Copy link

So I ran Node 8 and didn't see this problem crop up from running both dev and production builds. If anyone has a few repro steps it will make patching manifest-webpack-plugin (if needed) way easier.

@petetnt
Copy link
Contributor

petetnt commented May 13, 2018

If I'd have to guess something is screwy with process.env.PUBLIC_URL in the CI

@bugzpodder
Copy link

bugzpodder commented May 13, 2018

@TheLarkInn I was able to get a repro.

[--- EDIT: this PR was merged to the create-react-app's next branch ---]
You'll need to apply the following patch:
#4454
[--- EDIT: this PR was merged to the create-react-app's next branch ---]

and run "yarn e2e:docker --interactive --test-suite kitchensink"

$ react-scripts build                                                                                                                                  
Creating an optimized production build...
WARNING: The `text-hide()` mixin has been deprecated as of v4.1.0. It will be removed entirely in v5.
         on line 10 of node_modules/bootstrap/scss/mixins/_text-hide.scss, in mixin `text-hide`
         from line 57 of node_modules/bootstrap/scss/utilities/_text.scss
         from line 14 of node_modules/bootstrap/scss/_utilities.scss
         from line 41 of node_modules/bootstrap/scss/bootstrap.scss
         from line 1 of stdin

path.js:7
    throw new TypeError('Path must be a string. Received ' + inspect(path));
    ^

TypeError: Path must be a string. Received undefined
    at assertPath (path.js:7:11)
    at Object.basename (path.js:1361:5)
    at moduleAsset (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/webpack-manifest-plugin/lib/plugin.js:45:12)
    at SyncHook.eval (eval at create (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/tapable/lib/HookCodeFactory.js:51:12), <anonymous>:7:1)
    at Compilation.createModuleAssets (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/webpack/lib/Compilation.js:1768:29)
    at hooks.optimizeTree.callAsync.err (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/webpack/lib/Compilation.js:939:9)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/tapable/lib/Hook.js:35:21)
    at Compilation.seal (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/webpack/lib/Compilation.js:890:27)
    at hooks.make.callAsync.err (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/webpack/lib/Compiler.js:481:17)
    at _done (eval at create (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:9:1)
    at _err1 (eval at create (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:32:22)
    at _addModuleChain (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/webpack/lib/Compilation.js:758:12)
    at processModuleDependencies.err (/tmp/tmp.tgGgDwkWWZ/test-kitchensink/node_modules/webpack/lib/Compilation.js:697:9)
    at _combinedTickCallback (internal/process/next_tick.js:73:7)
    at process._tickCallback (internal/process/next_tick.js:104:9)
error Command failed with exit code 1.                                                                                                                 
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.                                                                   
++ set +x
e2e-kitchensink.sh: ERROR! An error was encountered executing line 128.
Cleaning up.
yarn config v1.6.0                                                                                                                                     
success Set "registry" to "https://registry.yarnpkg.com".                                                                                              
Done in 0.09s.                                                                                                                                         
Exiting with error.

@TheLarkInn
Copy link

TheLarkInn commented May 14, 2018 via email

@DanProudfoot
Copy link

Just a little thing I've seen in playing with this - and I don't know if this is a webpack 4 thing, a more general CRA 2.0 thing, or something I've done - but I've found that importing libraries that bundle both frontend and node code seems to pick the node version in some cases.

I've found this problem with both firebase and socket.io, but I think there may be others.

@gaearon
Copy link
Contributor

gaearon commented May 15, 2018

@DanProudfoot File a new issue pls?

- [x] Utilize webpack 4 development and production modes
- [x] Upgrade webpack dev server
- [x] Webpack 4 compatible release of thread-loader
- [x] Webpack 4 compatible release of HtmlWebpackPlugin
- [x] Webpack 4 compatible release of SwPrecacheWebpackPlugin
- [x] Webpack 4 compatible release of WebpackManifestPlugin
- [x] Update README
- [x] Update WebpackDevServerUtils
- [x] Update InterpolateHtmlPlugin
- [x] Update ModuleScopePlugin
- [x] Update WatchMissingNodeModulesPlugin
- [x] Move UglifyJS options to webpack 4 optimize
- [x] Move InterpolateHtmlPlugin to make it tapable on HtmlWebpackPlugin
- [x] vendor splitting via splitChunks.splitChunks (https://twitter.com/wSokra/status/969633336732905474)
- [x] long term caching via splitChunks.runtimeChunk (https://twitter.com/wSokra/status/969679223278505985)
- [x] Make sure process.env.NODE_ENV is proxied correctly to `react-error-overlay`
- [x] Implicit webpack.NamedModulesPlugin in dev config as its default in webpack 4
- [x] Disable webpack performance hints as we have our own filesize reporter
- [x] Replace ExtractTextPlugin with MiniCssExtractPlugin
- [x] Switch to css whole file minification via OptimizeCSSAssetsPlugin rather than per module css minification to gain performance
@andriijas
Copy link
Contributor Author

@bugzpodder i dont have docker available right now - can you debug why the manifest gets path undefined? thanks 🥂

@DanProudfoot
Copy link

DanProudfoot commented May 15, 2018

@gaearon I've forked and mucked around with things so much, I'm not totally confident it's not my fault. I guess I should have caveated that more.

I guess I should probably ask, has anyone using this in a more "stock" config experienced something similar?

Sorry all, it was my own stupid fault - I was using directory-named-webpack-plugin, and that breaks things if I don't set honorPackage: false

Sorry all!

@bugzpodder
Copy link

bugzpodder commented May 15, 2018

Did a bit of debugging.
You'll want to remove this line: rm -rf "$temp_app_path" "$temp_module_path" || $CI
from the e2e-kitchensink script before trying to run yarn e2e:docker --interactive --test-suite kitchensink so you get your temp dir preserved.
You may also want to remove absoluteLoad import before trying yarn build as it seem to be an unrelated issue (src/features/env/NodePath.js) otherwise set the NODE_PATH correctly before building.

For the actual manifest plugin issue from webpack build,

Creating an optimized production build...
/tmp/tmp.VwSgCdpDNy/test-kitchensink/build /tmp/tmp.VwSgCdpDNy/test-kitchensink/build/asset-manifest.json asset-manifest.json
static/media/logo.5d5d9eef.svg /tmp/tmp.VwSgCdpDNy/test-kitchensink/src/features/webpack/assets/logo.svg
WARNING: The `text-hide()` mixin has been deprecated as of v4.1.0. It will be removed entirely in v5.
         on line 10 of node_modules/bootstrap/scss/mixins/_text-hide.scss, in mixin `text-hide`
         from line 57 of node_modules/bootstrap/scss/utilities/_text.scss
         from line 14 of node_modules/bootstrap/scss/_utilities.scss
         from line 41 of node_modules/bootstrap/scss/bootstrap.scss
         from line 1 of stdin

static/media/aFileWithExt.9753d3ac.unknown /tmp/tmp.VwSgCdpDNy/test-kitchensink/src/features/webpack/assets/aFileWithExt.unknown
static/media/logo.5d5d9eef.svg /tmp/tmp.VwSgCdpDNy/test-kitchensink/src/features/webpack/assets/logo.svg (this is NomalModule, its userRequest field is the second token being outputted) 
static/media/aFileWithoutExt.cb7eb057.bin /tmp/tmp.VwSgCdpDNy/test-kitchensink/src/features/webpack/assets/aFileWithoutExt
static/media/logo.5d5d9eef.svg undefined (this is ConcatenatedModule, its userRequest field is undefined) -- this is from src/features/webpack/SvgComponent.js
path.js:7
    throw new TypeError('Path must be a string. Received ' + inspect(path));
    ^

TypeError: Path must be a string. Received undefined

@pelotom pelotom mentioned this pull request May 17, 2018
@bugzpodder bugzpodder mentioned this pull request May 20, 2018
@gaearon gaearon merged commit d72678f into facebook:next May 20, 2018
@gaearon
Copy link
Contributor

gaearon commented May 20, 2018

@gaearon
Copy link
Contributor

gaearon commented May 20, 2018

I filed #4492 for follow-up discussion.
Please post your thoughts!

@marcofugaro
Copy link
Contributor

Great job! 🎉
Could you publish the updated react-dev-utils on the 6.0.0-next tag?

@wregis
Copy link

wregis commented May 21, 2018

Well done, guys!
Any idea when it will be available on npm?

@Ayc0
Copy link

Ayc0 commented May 21, 2018

GG @andriijas

@gaearon
Copy link
Contributor

gaearon commented May 21, 2018

As usual for 2.x alphas, we'll post about new releases in #3815.
Subscribe to that issue.

I'm locking this one to reduce the notification noise. Thanks again to everyone!

@facebook facebook locked as resolved and limited conversation to collaborators May 21, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.