-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert to separate bundles for server vs. client rendering with HMR
1. Turned back on hmr and inline in webpacker.yml to support HMR. 2. Change config/initializers/react_on_rails.rb to have the correct server bundle name 3. Follow the flow from config/webpack/development.js to webpackConfig.js and consider uncommenting the debug line to see what happens when you run bin/webpack --debug
- Loading branch information
Showing
13 changed files
with
208 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,5 @@ | ||
# Procfile for development using HMR | ||
|
||
web: rails s -p 3000 | ||
|
||
# Note, hot and live reloading don't work with the default generator setup on | ||
# top of the rails/webpacker Webpack config with server rendering. | ||
# If you have server rendering enabled (prerender is true), you either need to | ||
# a. Ensure that you have dev_server.hmr and dev_server.inline BOTH set to false, | ||
# and you have this option in your config/initializers/react_on_rails.rb: | ||
# config.same_bundle_for_client_and_server = true | ||
# If you have either config/webpacker.yml option set to true, you'll see errors like | ||
# "ReferenceError: window is not defined" (if hmr is true) | ||
# "TypeError: Cannot read property 'prototype' of undefined" (if inline is true) | ||
# b. Skip using the webpack-dev-server. bin/webpack --watch is typically | ||
fast enough. | ||
# c. See the React on Rails README for a link to documentation for how to setup | ||
# SSR with HMR and React hot loading using the webpack-dev-server only for the | ||
# client bundles and a static file for the server bundle. | ||
|
||
# Run the webpack-dev-server for client and maybe server files | ||
webpack-dev-server: bin/webpack-dev-server | ||
|
||
# Keep the JS fresh for server rendering. Remove if not server rendering. | ||
# Especially if you have not configured generation of a server bundle without a hash. | ||
# as that will conflict with the manifest created by the bin/webpack-dev-server | ||
# rails-server-assets: SERVER_BUNDLE_ONLY=yes bin/webpack --watch | ||
# You can run these commands in separate shells | ||
rails: bundle exec rails s -p 3000 | ||
wp-client: bin/webpack-dev-server | ||
wp-server: SERVER_BUNDLE_ONLY=yes bin/webpack --watch |
5 changes: 5 additions & 0 deletions
5
app/javascript/bundles/HelloWorld/components/HelloWorldServer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import HelloWorld from './HelloWorld'; | ||
// This could be specialized for server rendering | ||
// For example, if using React-Router, we'd have the SSR setup here. | ||
|
||
export default HelloWorld; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import ReactOnRails from 'react-on-rails'; | ||
|
||
import HelloWorld from '../bundles/HelloWorld/components/HelloWorldServer'; | ||
|
||
// This is how react_on_rails can see the HelloWorld in the browser. | ||
ReactOnRails.register({ | ||
HelloWorld, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
const { merge } = require('webpack-merge') | ||
const environment = require('./environment') | ||
|
||
const configureClient = () => { | ||
const clientConfigObject = environment.toWebpackConfig() | ||
|
||
// Copy the object using merge b/c the clientConfigObject is non-stop mutable | ||
// After calling toWebpackConfig, and then modifying the resulting object, | ||
// another call to `toWebpackConfig` on this same environment will overwrite | ||
// the next line. | ||
const clientConfig = merge({}, clientConfigObject) | ||
|
||
// server-bundle is special and should ONLY be built by the serverConfig | ||
// In case this entry is not deleted, a very strange "window" not found | ||
// error shows referring to window["webpackJsonp"]. That is because the | ||
// client config is going to try to load chunks. | ||
delete clientConfig.entry['server-bundle'] | ||
|
||
return clientConfig | ||
} | ||
|
||
module.exports = configureClient |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,39 @@ | ||
process.env.NODE_ENV = process.env.NODE_ENV || 'development' | ||
|
||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); | ||
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); | ||
const path = require("path"); | ||
const webpackConfig = require('./webpackConfig') | ||
|
||
const environment = require('./environment') | ||
module.exports = webpackConfig() | ||
|
||
const isWebpackDevServer = process.env.WEBPACK_DEV_SERVER; | ||
const developmentOnly = () => { | ||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); | ||
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); | ||
const path = require("path"); | ||
|
||
const environment = require('./environment') | ||
|
||
const isWebpackDevServer = process.env.WEBPACK_DEV_SERVER; | ||
|
||
//plugins | ||
if (isWebpackDevServer) { | ||
environment.plugins.append( | ||
'ReactRefreshWebpackPlugin', | ||
new ReactRefreshWebpackPlugin({ | ||
overlay: { | ||
sockPort: 3035 | ||
} | ||
}) | ||
); | ||
} | ||
|
||
//plugins | ||
if (isWebpackDevServer) { | ||
environment.plugins.append( | ||
'ReactRefreshWebpackPlugin', | ||
new ReactRefreshWebpackPlugin({ | ||
overlay: { | ||
sockPort: 3035 | ||
} | ||
"ForkTsCheckerWebpackPlugin", | ||
new ForkTsCheckerWebpackPlugin({ | ||
typescript: { | ||
configFile: path.resolve(__dirname, "../../tsconfig.json"), | ||
}, | ||
async: false, | ||
}) | ||
); | ||
} | ||
|
||
|
||
environment.plugins.append( | ||
"ForkTsCheckerWebpackPlugin", | ||
new ForkTsCheckerWebpackPlugin({ | ||
typescript: { | ||
configFile: path.resolve(__dirname, "../../tsconfig.json"), | ||
}, | ||
async: false, | ||
}) | ||
); | ||
|
||
module.exports = environment.toWebpackConfig() | ||
module.exports = webpackConfig(developmentOnly) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,10 @@ | ||
process.env.NODE_ENV = process.env.NODE_ENV || 'production' | ||
|
||
// Below code should get refactored but the current way that rails/webpacker v5 | ||
// does the globals, it's tricky | ||
const environment = require('./environment') | ||
const webpackConfig = require('./webpackConfig') | ||
|
||
const productionOnly = () => { | ||
// place any code here that is for production only | ||
} | ||
|
||
module.exports = environment.toWebpackConfig() | ||
module.exports = webpackConfig(productionOnly) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
const { config } = require('@rails/webpacker') | ||
const webpack = require('webpack') | ||
const { merge } = require('webpack-merge') | ||
const environment = require('./environment') | ||
|
||
const clientConfigObject = environment.toWebpackConfig() | ||
|
||
const configureServer = () => { | ||
// We need to use "merge" because the clientConfigObject, EVEN after running | ||
// toWebpackConfig() is a mutable GLOBAL. Thus any changes, like modifying the | ||
// entry value will result in changing the client config! | ||
// Using webpack-merge into an empty object avoids this issue. | ||
const serverWebpackConfig = merge({}, clientConfigObject) | ||
|
||
// We just want the single server bundle entry | ||
const serverEntry = { | ||
'server-bundle': environment.entry.get('server-bundle'), | ||
} | ||
serverWebpackConfig.entry = serverEntry | ||
|
||
// Remove the mini-css-extract-plugin from the style loaders because | ||
// the client build will handle exporting CSS. | ||
// replace file-loader with null-loader | ||
serverWebpackConfig.module.rules.forEach((loader) => { | ||
if (loader.use && loader.use.filter) { | ||
loader.use = loader.use.filter( | ||
(item) => | ||
!(typeof item === 'string' && item.match(/mini-css-extract-plugin/)) | ||
) | ||
} | ||
}) | ||
|
||
// No splitting of chunks for a server bundle | ||
serverWebpackConfig.optimization = { | ||
minimize: false, | ||
} | ||
serverWebpackConfig.plugins.unshift( | ||
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }) | ||
) | ||
|
||
// Custom output for the server-bundle that matches the config in | ||
// config/initializers/react_on_rails.rb | ||
serverWebpackConfig.output = { | ||
filename: 'server-bundle.js', | ||
globalObject: 'this', | ||
// If using the React on Rails Pro node server renderer, uncomment the next line | ||
// libraryTarget: 'commonjs2', | ||
path: config.outputPath, | ||
publicPath: config.outputPath, | ||
// https://webpack.js.org/configuration/output/#outputglobalobject | ||
} | ||
|
||
// Don't hash the server bundle b/c would conflict with the client manifest | ||
// And no need for the MiniCssExtractPlugin | ||
serverWebpackConfig.plugins = serverWebpackConfig.plugins.filter( | ||
(plugin) => | ||
plugin.constructor.name !== 'WebpackAssetsManifest' && | ||
plugin.constructor.name !== 'MiniCssExtractPlugin' && | ||
plugin.constructor.name !== 'ForkTsCheckerWebpackPlugin' | ||
) | ||
|
||
// Critical due to https://github.com/rails/webpacker/pull/2644 | ||
delete serverWebpackConfig.devServer | ||
|
||
// eval works well for the SSR bundle because it's the fastest and shows | ||
// lines in the server bundle which is good for debugging SSR | ||
// The default of cheap-module-source-map is slow and provides poor info. | ||
serverWebpackConfig.devtool = 'eval' | ||
|
||
// If using the default 'web', then libraries like Emotion and loadable-components | ||
// break with SSR. The fix is to use a node renderer and change the target. | ||
// If using the React on Rails Pro node server renderer, uncomment the next line | ||
// serverWebpackConfig.target = 'node' | ||
|
||
return serverWebpackConfig | ||
} | ||
|
||
module.exports = configureServer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
process.env.NODE_ENV = process.env.NODE_ENV || 'development' | ||
const webpackConfig = require('./webpackConfig') | ||
|
||
const environment = require('./environment') | ||
const testOnly = () => { | ||
// place any code here that is for test only | ||
} | ||
|
||
module.exports = environment.toWebpackConfig() | ||
module.exports = webpackConfig(testOnly) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
const clientConfig = require('./clientWebpackConfiguration') | ||
const serverConfig = require('./serverWebpackConfiguration') | ||
|
||
const webpackConfig = (envSpecific) => { | ||
if (envSpecific) { | ||
envSpecific() | ||
} | ||
|
||
let result | ||
// For HMR, need to separate the the client and server webpack configurations | ||
if (process.env.WEBPACK_DEV_SERVER || process.env.CLIENT_BUNDLE_ONLY) { | ||
// eslint-disable-next-line no-console | ||
console.log('[React on Rails] Creating only the client bundles.') | ||
result = clientConfig() | ||
} else if (process.env.SERVER_BUNDLE_ONLY) { | ||
// eslint-disable-next-line no-console | ||
console.log('[React on Rails] Creating only the server bundle.') | ||
result = serverConfig() | ||
} else { | ||
// default is the standard client and server build | ||
// eslint-disable-next-line no-console | ||
console.log('[React on Rails] Creating both client and server bundles.') | ||
result = [clientConfig(), serverConfig()] | ||
} | ||
|
||
// To debug, uncomment next line and inspect "result" | ||
// debugger | ||
return result | ||
} | ||
|
||
module.exports = webpackConfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters