Hot reloading doesn't work in 2.0
mrchief opened this issue ยท 11 comments
- Node Version: 8.9.4
- NPM Version: 5.2.0
- koa Version: ^2.3.0
- koa-webpack: ^2.0.3
v2 webpack.config
// webpack.config.js
'use strict' // eslint-disable-line
const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const StyleLintPlugin = require('stylelint-webpack-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const { CSSModules, inlineLimit } = require('./config')
const nodeEnv = process.env.NODE_ENV || 'development'
const isDev = nodeEnv !== 'production'
const WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin')
const webpackIsomorphicToolsPlugin = new WebpackIsomorphicToolsPlugin(require('./WIT.config')).development(isDev)
// Setting the plugins for development/production
const getPlugins = () => {
// Common
const plugins = [
new ExtractTextPlugin({
filename: '[name].[contenthash:8].css',
allChunks: true,
disable: isDev, // Disable css extracting on development
ignoreOrder: CSSModules
}),
new webpack.LoaderOptionsPlugin({
options: {
// Javascript lint
eslint: {},
context: '/', // Required for the sourceMap of css/sass loader
debug: isDev,
minimize: !isDev
}
}),
// Style lint
new StyleLintPlugin({ files: ['src/**/*.css'], quiet: false }),
// Setup environment variables for client
new webpack.EnvironmentPlugin({ NODE_ENV: JSON.stringify(nodeEnv) }),
// Setup global variables for client
new webpack.DefinePlugin({
__CLIENT__: true,
__SERVER__: false,
__DEV__: isDev
}),
new webpack.NoEmitOnErrorsPlugin(),
webpackIsomorphicToolsPlugin,
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: module => /node_modules/.test(module.resource) })
]
if (isDev) {
// For development
plugins.push(
// Prints more readable module names in the browser console on HMR updates
new webpack.NamedModulesPlugin(),
new webpack.IgnorePlugin(/webpack-stats\.json$/),
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false
})
)
} else {
plugins.push(
// For production
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
beautify: false,
mangle: { screw_ie8: true },
compress: {
screw_ie8: true, // React doesn't support IE8
warnings: false,
unused: true,
dead_code: true
},
output: { screw_ie8: true, comments: false }
})
)
}
return plugins
}
// get babel plugins for dev/production
const getBabelPlugins = () => {
const plugins = [
'react-hot-loader/babel',
'transform-object-rest-spread',
'transform-class-properties',
[
'transform-imports',
{
'redux-form': {
transform: 'redux-form/es/${member}', // eslint-disable-line no-template-curly-in-string
preventFullImport: true
},
lodash: {
transform: 'lodash/${member}', // eslint-disable-line no-template-curly-in-string
preventFullImport: true
}
}
]
]
if (isDev) {
plugins.push('transform-react-jsx-source')
}
return plugins
}
// Setting the entry for development/production
const getEntry = () => {
// For development
let entry = [
'babel-polyfill', // Support promise for IE browser (for dev)
'react-hot-loader/patch',
'./src/client.js'
]
// For production
if (!isDev) {
entry = {
main: './src/client.js'
}
}
return entry
}
// Setting webpack config
module.exports = {
name: 'client',
target: 'web',
cache: isDev,
devtool: isDev ? 'cheap-module-eval-source-map' : 'hidden-source-map',
context: path.join(process.cwd()),
entry: getEntry(),
output: {
path: path.join(process.cwd(), './build/public/assets'),
publicPath: '/assets/',
// Don't use chunkhash in development it will increase compilation time
filename: isDev ? '[name].js' : '[name].[chunkhash:8].js',
chunkFilename: isDev ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js',
pathinfo: isDev
},
module: {
noParse: [/dtrace-provider/, /safe-json-stringify/, /mv/, /source-map-support/],
rules: [
{
test: /\.jsx?$/,
enforce: 'pre',
exclude: /node_modules/,
loader: 'eslint',
options: {
fix: true
}
},
{
test: /node_modules[/\\]jsonstream/i,
loader: 'shebang'
},
{
test: /\.jsx?$/,
loader: 'babel',
exclude: /node_modules/,
options: {
cacheDirectory: isDev,
babelrc: false,
presets: [
[
'env',
{
targets: {
browsers: ['last 2 versions', 'ie >= 10']
},
useBuiltIns: true
}
],
'react'
],
plugins: getBabelPlugins()
}
},
{
test: /\.css$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract({
fallback: 'style',
use: [
{
loader: 'css',
options: {
importLoaders: 1,
sourceMap: true,
modules: CSSModules,
// "context" and "localIdentName" need to be the same with server config,
// or the style will flick when page first loaded
context: path.join(process.cwd(), './src'),
localIdentName: isDev ? '[name]__[local].[hash:base64:5]' : '[hash:base64:5]',
minimize: !isDev
}
},
{
loader: 'postcss'
}
]
})
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader'
}
]
}),
include: [/node_modules[/\\]animate.css/,
/flexboxgrid/,
/react-table/,
/react-datepicker/,
/react-checkbox/
]
},
{
test: /\.(woff2?|ttf|eot|svg)$/,
loader: 'url',
options: { limit: inlineLimit }
},
{
test: webpackIsomorphicToolsPlugin.regular_expression('images'),
// Any image below or equal to 10K will be converted to inline base64 instead
use: [
{
loader: 'url',
options: { limit: inlineLimit }
},
// Using for image optimization
{
loader: 'image-webpack',
options: { bypassOnDebug: true }
}
]
}
]
},
plugins: getPlugins(),
// Where to resolve our loaders
resolveLoader: {
modules: ['src', 'node_modules'],
moduleExtensions: ['-loader']
},
resolve: {
modules: ['src', 'node_modules'],
descriptionFiles: ['package.json'],
moduleExtensions: ['-loader'],
extensions: ['.js', '.jsx', '.json']
},
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
// https://webpack.github.io/docs/configuration.html#node
// https://github.com/webpack/node-libs-browser/tree/master/mock
node: {
fs: 'empty',
vm: 'empty',
net: 'empty',
tls: 'empty'
}
}
v2 app.js
// app.js
const config = require('../tools/webpack/webpack.client.babel')
app.use(require('koa-webpack')({
config,
dev: {
publicPath: config.output.publicPath,
noInfo: true,
stats: 'minimal'
},
hot: { port: 3001 } // this bit was added after 2.x migration as the default port 8081 is taken up in my PC
}))
Expected Behavior
Hot reloading should work. Browser should show HMR connected in console.
Actual Behavior
Doesn't work. I neither see HMR connected message nor does the browser refreshes when I change source files. Webpack builds the changes.
How can we reproduce the behavior?
The same config (minus changes required for v2.x migration) worked without any issues with v1. Here are the relevant bits:
v1 webpack config
'use strict' // eslint-disable-line
const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const StyleLintPlugin = require('stylelint-webpack-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const { CSSModules, inlineLimit } = require('./config')
const nodeEnv = process.env.NODE_ENV || 'development'
const isDev = nodeEnv !== 'production'
const WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin')
const webpackIsomorphicToolsPlugin = new WebpackIsomorphicToolsPlugin(require('./WIT.config')).development(isDev)
// Setting the plugins for development/production
const getPlugins = () => {
// Common
const plugins = [
new ExtractTextPlugin({
filename: '[name].[contenthash:8].css',
allChunks: true,
disable: isDev, // Disable css extracting on development
ignoreOrder: CSSModules
}),
new webpack.LoaderOptionsPlugin({
options: {
// Javascript lint
eslint: {},
context: '/', // Required for the sourceMap of css/sass loader
debug: isDev,
minimize: !isDev
}
}),
// Style lint
new StyleLintPlugin({ files: ['src/**/*.css'], quiet: false }),
// Setup environment variables for client
new webpack.EnvironmentPlugin({ NODE_ENV: JSON.stringify(nodeEnv) }),
// Setup global variables for client
new webpack.DefinePlugin({
__CLIENT__: true,
__SERVER__: false,
__DEV__: isDev
}),
new webpack.NoEmitOnErrorsPlugin(),
webpackIsomorphicToolsPlugin,
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: module => /node_modules/.test(module.resource) })
]
if (isDev) {
// For development
plugins.push(
new webpack.HotModuleReplacementPlugin(),
// Prints more readable module names in the browser console on HMR updates
new webpack.NamedModulesPlugin(),
new webpack.IgnorePlugin(/webpack-stats\.json$/),
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false
})
)
} else {
plugins.push(
// For production
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
beautify: false,
mangle: { screw_ie8: true },
compress: {
screw_ie8: true, // React doesn't support IE8
warnings: false,
unused: true,
dead_code: true
},
output: { screw_ie8: true, comments: false }
})
)
}
return plugins
}
// get babel plugins for dev/production
const getBabelPlugins = () => {
const plugins = [
'react-hot-loader/babel',
'transform-object-rest-spread',
'transform-class-properties',
[
'transform-imports',
{
'redux-form': {
transform: 'redux-form/es/${member}', // eslint-disable-line no-template-curly-in-string
preventFullImport: true
},
lodash: {
transform: 'lodash/${member}', // eslint-disable-line no-template-curly-in-string
preventFullImport: true
}
}
]
]
if (isDev) {
plugins.push('transform-react-jsx-source')
}
return plugins
}
// Setting the entry for development/production
const getEntry = () => {
// For development
let entry = [
'babel-polyfill', // Support promise for IE browser (for dev)
'react-hot-loader/patch',
'webpack-hot-middleware/client?reload=true',
'./src/client.js'
]
// For production
if (!isDev) {
entry = {
main: './src/client.js'
}
}
return entry
}
// Setting webpack config
module.exports = {
name: 'client',
target: 'web',
cache: isDev,
devtool: isDev ? 'cheap-module-eval-source-map' : 'hidden-source-map',
context: path.join(process.cwd()),
entry: getEntry(),
output: {
path: path.join(process.cwd(), './build/public/assets'),
publicPath: '/assets/',
// Don't use chunkhash in development it will increase compilation time
filename: isDev ? '[name].js' : '[name].[chunkhash:8].js',
chunkFilename: isDev ? '[name].chunk.js' : '[name].[chunkhash:8].chunk.js',
pathinfo: isDev
},
module: {
noParse: [/dtrace-provider/, /safe-json-stringify/, /mv/, /source-map-support/],
rules: [
{
test: /\.jsx?$/,
enforce: 'pre',
exclude: /node_modules/,
loader: 'eslint',
options: {
fix: true
}
},
{
test: /node_modules[/\\]jsonstream/i,
loader: 'shebang'
},
{
test: /\.jsx?$/,
loader: 'babel',
exclude: /node_modules/,
options: {
cacheDirectory: isDev,
babelrc: false,
presets: [
[
'env',
{
targets: {
browsers: ['last 2 versions', 'ie >= 10']
},
useBuiltIns: true
}
],
'react'
],
plugins: getBabelPlugins()
}
},
{
test: /\.css$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract({
fallback: 'style',
use: [
{
loader: 'css',
options: {
importLoaders: 1,
sourceMap: true,
modules: CSSModules,
// "context" and "localIdentName" need to be the same with server config,
// or the style will flick when page first loaded
context: path.join(process.cwd(), './src'),
localIdentName: isDev ? '[name]__[local].[hash:base64:5]' : '[hash:base64:5]',
minimize: !isDev
}
},
{
loader: 'postcss'
}
]
})
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader'
}
]
}),
include: [/node_modules[/\\]animate.css/,
/flexboxgrid/,
/react-table/,
/react-datepicker/,
/react-checkbox/
]
},
{
test: /\.(woff2?|ttf|eot|svg)$/,
loader: 'url',
options: { limit: inlineLimit }
},
{
test: webpackIsomorphicToolsPlugin.regular_expression('images'),
// Any image below or equal to 10K will be converted to inline base64 instead
use: [
{
loader: 'url',
options: { limit: inlineLimit }
},
// Using for image optimization
{
loader: 'image-webpack',
options: { bypassOnDebug: true }
}
]
}
]
},
plugins: getPlugins(),
// Where to resolve our loaders
resolveLoader: {
modules: ['src', 'node_modules'],
moduleExtensions: ['-loader']
},
resolve: {
modules: ['src', 'node_modules'],
descriptionFiles: ['package.json'],
moduleExtensions: ['-loader'],
extensions: ['.js', '.jsx', '.json']
},
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
// https://webpack.github.io/docs/configuration.html#node
// https://github.com/webpack/node-libs-browser/tree/master/mock
node: {
fs: 'empty',
vm: 'empty',
net: 'empty',
tls: 'empty'
}
}
v1 app.js
// app.js
const config = require('../tools/webpack/webpack.client.babel')
app.use(require('koa-webpack')({
config,
dev: {
publicPath: config.output.publicPath,
noInfo: true,
stats: 'minimal'
}
}))
Dunno what to tell you. Works great on my end. Must be an environment or config issue. Might want to start from a simpler config and build it back up to identify the issue.
You mean you have a koa app using react and hot reloading works for you? Mind sharing some code?
There is literally 3 changed lines between v1 and v2 config/app.js. So I'm puzzled as to what I'm missing.
I don't use or work with React. So you or someone else who uses that is going to have to debug the reasons why it's not working as expected. I can tell you however, that the create-react-app folks are using this module via a not-as-yet-public project with success.
I can tell you however, that the create-react-app folks are using this module via a not-as-yet-public project with success.
I heard those rumors too. :)
I tried searching but didn't find any boilerplate that uses webpack-hot-client. Almost everyone seems to be using webpack-hot-middleware and that works like a charm in my case too. I'll keep looking though. Let me know if you stumble upon anything.
Meanwhile, will you be able to share a gist of your working config? Just the config and app.js will suffice. Maybe I'd be able to glean something off it?
Same problem here. Websocket is up but the client is not listening. Although I'm not using React, and I'm not sure that matters(?).
There is no WebSocket Client Connected
in the log. But if I connect manually the log message pop up. So I think that the javascript that is suppose to connect to the websocket is never given to the client for some reason
There are more flags too: if you pass the koa server to webpack-hot-client, it gives you some weird error.
Almost everyone seems to be using webpack-hot-middleware and that works like a charm in my case too.
You're not required to use this module if you'd prefer webpack-hot-middleware, and I don't mean that in a snarky way. You can also use the older version. webpack-hot-middleware
is a much older approach that relies on some older features of browsers before WebSockets were available. webpack-hot-client also utilizes webpack internals to take some work off the shoulders of devs. Mentioned at the bottom of the readme is also koa-webpack-middleware, which is the super old project an earlier version of this module was forked from. If the current line of koa-webpack isn't your bag or your preference, you have a bunch of options.
Meanwhile, will you be able to share a gist of your working config? Just the config and app.js will suffice. Maybe I'd be able to glean something off it?
Both the tests for koa-webpack and webpack-hot-client contain configs that work perfectly fine. Again, I recommend you start with a simple config and build it back up to the current, rather complex state you have it in now. Going step by step in adding complexity and components onto a config should yield the issue or the culprit, and it should be a relatively quick process.
You can also examine your build output in the terminal to verify that the webpack-hot-client/client
script was added to your bundle. If it wasn't, nothing will happen in the client.
There are more flags too: if you pass the koa server to webpack-hot-client, it gives you some weird error.
I dunno what that means, but if an option doesn't work correctly you should open a new issue rather than reporting it in this thread.
@MichaelBergquistSuarez since you're adding a comment here with a difference scenario than the OP, you should open a new issue and provide the code the issue template requests. That comment doesn't add much value or help to triage the original post.
You're not required to use this module if you'd prefer webpack-hot-middleware, and I don't mean that in a snarky way.
I came to using this module since it saved me effort from using those two.
You can also use the older version.
Its what I'm doing right now. But I'd like to be able to use the new version. I like this module and reported this only because I care about it.
Again, I recommend you start with a simple config and build it back up to the current, rather complex state you have it in now.
I only pasted everything for completeness. The relevant bits are few lines. I'll try to create a repo with simplified config.
I just published a patch version to webpack-hot-client that might resolve one or more issues you all are facing. Please do let me know if that works. Will probably require a reinstall of koa-webpack.
Success! Good work. Now the client connects to the socket, at least on my part (not using React though).
Using the master
branch from both koa-webpack
and webpack-hot-client