feat(react): add react fast refresh to webpack (#5612)

This commit is contained in:
Emily Xiong 2021-05-18 15:47:18 -04:00 committed by GitHub
parent 4a0dcbab40
commit b8584a6f18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 133 additions and 20 deletions

View File

@ -26,6 +26,14 @@ Type: `string`
Target which builds the application
### hmr
Default: `false`
Type: `boolean`
Enable hot module replacement.
### host
Default: `localhost`

View File

@ -34,6 +34,10 @@ The options below are common to the `serve` command used within an Nx workspace.
This option allows you to whitelist services that are allowed to access the dev server.
### hmr
Enable hot module replacement.
### host
Host to listen on.
@ -140,10 +144,6 @@ Don't verify connected clients are part of allowed hosts.
Output in-file eval sourcemaps.
### hmr
Enable hot module replacement.
### hmr-warning
Show a warning when the `--hmr` option is enabled.

View File

@ -27,6 +27,14 @@ Type: `string`
Target which builds the application
### hmr
Default: `false`
Type: `boolean`
Enable hot module replacement.
### host
Default: `localhost`

View File

@ -34,6 +34,10 @@ The options below are common to the `serve` command used within an Nx workspace.
This option allows you to whitelist services that are allowed to access the dev server.
### hmr
Enable hot module replacement.
### host
Host to listen on.
@ -140,10 +144,6 @@ Don't verify connected clients are part of allowed hosts.
Output in-file eval sourcemaps.
### hmr
Enable hot module replacement.
### hmr-warning
Show a warning when the `--hmr` option is enabled.

View File

@ -27,6 +27,14 @@ Type: `string`
Target which builds the application
### hmr
Default: `false`
Type: `boolean`
Enable hot module replacement.
### host
Default: `localhost`

View File

@ -34,6 +34,10 @@ The options below are common to the `serve` command used within an Nx workspace.
This option allows you to whitelist services that are allowed to access the dev server.
### hmr
Enable hot module replacement.
### host
Host to listen on.
@ -140,10 +144,6 @@ Don't verify connected clients are part of allowed hosts.
Output in-file eval sourcemaps.
### hmr
Enable hot module replacement.
### hmr-warning
Show a warning when the `--hmr` option is enabled.

View File

@ -64,6 +64,7 @@ Options:
--sslCert SSL certificate to use for serving HTTPS.
--watch Watches for changes and rebuilds application (default: true)
--liveReload Whether to reload the page on change, using live-reload. (default: true)
--hmr Enable hot module replacement.
--publicHost Public URL where the application will be served
--open Open the application in the browser.
--allowedHosts This option allows you to whitelist services that are allowed to access the dev server.

View File

@ -34,6 +34,10 @@ The options below are common to the `serve` command used within an Nx workspace.
This option allows you to whitelist services that are allowed to access the dev server.
### hmr
Enable hot module replacement.
### host
Host to listen on.
@ -140,10 +144,6 @@ Don't verify connected clients are part of allowed hosts.
Output in-file eval sourcemaps.
### hmr
Enable hot module replacement.
### hmr-warning
Show a warning when the `--hmr` option is enabled.

View File

@ -68,6 +68,7 @@
"@nrwl/tao": "12.3.0",
"@nrwl/web": "12.3.0",
"@nrwl/workspace": "12.3.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
"@popperjs/core": "^2.9.2",
"@reduxjs/toolkit": "1.5.0",
"@rollup/plugin-babel": "5.0.2",
@ -189,6 +190,7 @@
"protractor": "5.4.3",
"raw-loader": "3.1.0",
"react-redux": "7.2.3",
"react-refresh": "^0.9.0",
"react-router-dom": "5.1.2",
"regenerator-runtime": "0.13.7",
"release-it": "^7.4.0",

View File

@ -37,11 +37,13 @@
"@nrwl/storybook": "*",
"@nrwl/web": "*",
"@nrwl/workspace": "*",
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
"@svgr/webpack": "^5.4.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.23.1",
"eslint-plugin-react-hooks": "^4.2.0",
"react-refresh": "^0.9.0",
"url-loader": "^3.0.0"
}
}

View File

@ -1,4 +1,5 @@
import { Configuration } from 'webpack';
import * as ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin';
// Add React-specific configuration
function getWebpackConfig(config: Configuration) {
@ -54,6 +55,27 @@ function getWebpackConfig(config: Configuration) {
}
);
if (config.mode === 'development' && config['devServer']?.hot) {
// add `react-refresh/babel` to babel loader plugin
const babelLoader = config.module.rules.find((rule) =>
rule.loader.toString().includes('babel-loader')
);
if (babelLoader) {
babelLoader.options['plugins'] = [
...(babelLoader.options['plugins'] || []),
[
require.resolve('react-refresh/babel'),
{
skipEnvCheck: true,
},
],
];
}
// add https://github.com/pmmmwh/react-refresh-webpack-plugin to webpack plugin
config.plugins.push(new ReactRefreshPlugin());
}
return config;
}

View File

@ -278,9 +278,11 @@ describe('app', () => {
expect(targetConfig.serve.executor).toEqual('@nrwl/web:dev-server');
expect(targetConfig.serve.options).toEqual({
buildTarget: 'my-app:build',
hmr: true,
});
expect(targetConfig.serve.configurations.production).toEqual({
buildTarget: 'my-app:build:production',
hmr: false,
});
});

View File

@ -112,10 +112,12 @@ function createServeTarget(options: NormalizedSchema): TargetConfiguration {
executor: '@nrwl/web:dev-server',
options: {
buildTarget: `${options.projectName}:build`,
hmr: true,
},
configurations: {
production: {
buildTarget: `${options.projectName}:build:production`,
hmr: false,
},
},
};

View File

@ -35,6 +35,7 @@ export interface WebDevServerOptions {
buildTarget: string;
open: boolean;
liveReload: boolean;
hmr: boolean;
watch: boolean;
allowedHosts: string;
maxWorkers?: number;

View File

@ -41,6 +41,11 @@
"description": "Whether to reload the page on change, using live-reload.",
"default": true
},
"hmr": {
"type": "boolean",
"description": "Enable hot module replacement.",
"default": false
},
"publicHost": {
"type": "string",
"description": "Public URL where the application will be served"

View File

@ -2,6 +2,7 @@ import { getDevServerConfig } from './devserver.config';
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import * as ts from 'typescript';
import * as fs from 'fs';
import { HotModuleReplacementPlugin } from 'webpack';
import { WebBuildBuilderOptions } from '../builders/build/build.impl';
import { WebDevServerOptions } from '../builders/dev-server/dev-server.impl';
import { join } from 'path';
@ -54,6 +55,7 @@ describe('getDevServerConfig', () => {
buildTarget: 'webapp:build',
ssl: false,
liveReload: true,
hmr: true,
open: false,
watch: true,
allowedHosts: null,
@ -328,7 +330,7 @@ describe('getDevServerConfig', () => {
root,
sourceRoot,
buildInput,
serveInput
{ ...serveInput, hmr: false }
);
expect(result.liveReload).toEqual(true);
@ -339,13 +341,49 @@ describe('getDevServerConfig', () => {
root,
sourceRoot,
buildInput,
{ ...serveInput, liveReload: false }
{ ...serveInput, hmr: false, liveReload: false }
);
expect(result.liveReload).toEqual(false);
});
});
describe('hmr option', () => {
it('should set the correct value', () => {
const { devServer: result } = getDevServerConfig(
root,
sourceRoot,
buildInput,
{ ...serveInput, hmr: false }
);
expect(result.hot).toEqual(false);
});
it('should set the correct if true and disable live reload', () => {
const { devServer: result } = getDevServerConfig(
root,
sourceRoot,
buildInput,
serveInput
);
expect(result.liveReload).toEqual(false);
expect(result.hot).toEqual(true);
});
it('should add hot module replacement plugin', () => {
const { plugins } = getDevServerConfig(
root,
sourceRoot,
buildInput,
serveInput
);
expect(plugins).toContainEqual(new HotModuleReplacementPlugin());
});
});
describe('ssl option', () => {
it('should set https to false if not on', () => {
const { devServer: result } = getDevServerConfig(

View File

@ -7,7 +7,7 @@ import { readFileSync } from 'fs';
import * as path from 'path';
import { getWebConfig } from './web.config';
import { Configuration } from 'webpack';
import { Configuration, HotModuleReplacementPlugin } from 'webpack';
import { WebBuildBuilderOptions } from '../builders/build/build.impl';
import { WebDevServerOptions } from '../builders/dev-server/dev-server.impl';
import { buildServePath } from './serve-path';
@ -31,6 +31,10 @@ export function getDevServerConfig(
serveOptions,
buildOptions
);
webpackConfig.plugins = [
...(webpackConfig.plugins || []),
getHmrPlugin(serveOptions),
].filter(Boolean);
return webpackConfig;
}
@ -85,7 +89,8 @@ function getDevServerPartial(
publicPath: servePath,
contentBase: false,
allowedHosts: [],
liveReload: options.liveReload,
liveReload: options.hmr ? false : options.liveReload, // disable liveReload if hmr is enabled
hot: options.hmr,
};
if (options.ssl && options.sslKey && options.sslCert) {
@ -114,3 +119,7 @@ function getProxyConfig(root: string, options: WebDevServerOptions) {
const proxyPath = path.resolve(root, options.proxyConfig as string);
return require(proxyPath);
}
function getHmrPlugin(options: WebDevServerOptions) {
return options.hmr && new HotModuleReplacementPlugin();
}

View File

@ -20519,6 +20519,11 @@ react-refresh@0.8.3, react-refresh@^0.8.3:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==
react-refresh@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
react-router-dom@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"