Skip to content

Commit 749c640

Browse files
jaysooFrozenPandaz
authored andcommitted
feat(react): add fast refresh support for webpack 5 (#6529)
1 parent b1eca69 commit 749c640

File tree

14 files changed

+283
-37
lines changed

14 files changed

+283
-37
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { readJson, Tree, updateJson } from '@nrwl/devkit';
2+
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
3+
import { nodeMigrateToWebpack5Generator } from './migrate-to-webpack-5';
4+
5+
describe('nodeMigrateToWebpack5Generator', () => {
6+
let tree: Tree;
7+
8+
beforeEach(() => {
9+
tree = createTreeWithEmptyWorkspace();
10+
updateJson(tree, 'package.json', (json) => {
11+
json.devDependencies = {
12+
'@nrwl/cli': '100.0.0',
13+
'@nrwl/jest': '100.0.0',
14+
'@nrwl/node': '100.0.0',
15+
'@nrwl/workspace': '100.0.0',
16+
};
17+
return json;
18+
});
19+
});
20+
21+
it('should add packages needed by Node', async () => {
22+
await nodeMigrateToWebpack5Generator(tree, {});
23+
24+
const json = readJson(tree, '/package.json');
25+
26+
expect(json.devDependencies['webpack']).toMatch(/\^5/);
27+
});
28+
29+
it('should add packages needed by Web if used', async () => {
30+
updateJson(tree, 'package.json', (json) => {
31+
json.devDependencies = {
32+
'@nrwl/cli': '100.0.0',
33+
'@nrwl/jest': '100.0.0',
34+
'@nrwl/node': '100.0.0',
35+
'@nrwl/react': '100.0.0',
36+
'@nrwl/web': '100.0.0',
37+
'@nrwl/workspace': '100.0.0',
38+
};
39+
return json;
40+
});
41+
42+
await nodeMigrateToWebpack5Generator(tree, {});
43+
44+
const json = readJson(tree, '/package.json');
45+
46+
expect(
47+
json.devDependencies['@pmmmwh/react-refresh-webpack-plugin']
48+
).toMatch(/^0\.5/);
49+
});
50+
});

packages/node/src/generators/migrate-to-webpack-5/migrate-to-webpack-5.ts

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,58 @@
11
import {
22
addDependenciesToPackageJson,
33
convertNxGenerator,
4+
GeneratorCallback,
45
logger,
6+
readJson,
7+
removeDependenciesFromPackageJson,
58
Tree,
69
} from '@nrwl/devkit';
10+
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
711

8-
const webpack5Packages = {
9-
'copy-webpack-plugin': '^9.0.0',
10-
'mini-css-extract-plugin': '^1.6.0',
11-
'source-map-loader': '^2.0.1',
12+
const basePackages = {
13+
'copy-webpack-plugin': '^9.0.1',
14+
webpack: '^5.47.0',
15+
'webpack-merge': '^5.8.0',
16+
'webpack-node-externals': '^3.0.0',
17+
};
18+
19+
const webPackages = {
20+
'mini-css-extract-plugin': '^2.1.0',
21+
'source-map-loader': '^3.0.0',
1222
'terser-webpack-plugin': '^5.1.1',
13-
webpack: '^5.39.1',
14-
'webpack-dev-server': '^3.11.2',
15-
'webpack-merge': '^5.7.3',
16-
'webpack-node-externals': '^2.5.2',
17-
'webpack-sources': '^2.2.0',
23+
'webpack-dev-server': '4.0.0-rc.0',
24+
'webpack-sources': '^3.0.2',
25+
'react-refresh': '^0.10.0',
26+
'@pmmmwh/react-refresh-webpack-plugin': '0.5.0-rc.2',
1827
};
1928

2029
export async function nodeMigrateToWebpack5Generator(tree: Tree, schema: {}) {
30+
let packages = basePackages;
31+
const tasks: GeneratorCallback[] = [];
32+
33+
const packageJson = readJson(tree, 'package.json');
34+
const deps = [
35+
...Object.keys(packageJson.dependencies), // just in case someone installed it here
36+
...Object.keys(packageJson.devDependencies),
37+
];
38+
39+
if (deps.includes('@nrwl/web')) {
40+
packages = {
41+
...packages,
42+
...webPackages,
43+
};
44+
}
45+
2146
logger.info(`NX Adding webpack 5 to workspace.`);
22-
return addDependenciesToPackageJson(tree, {}, webpack5Packages);
47+
48+
// Removing the packages ensures that the versions will be updated when adding them after
49+
tasks.push(
50+
removeDependenciesFromPackageJson(tree, [], Object.keys(packages))
51+
);
52+
53+
tasks.push(addDependenciesToPackageJson(tree, {}, packages));
54+
55+
return runTasksInSerial(...tasks);
2356
}
2457

2558
export default nodeMigrateToWebpack5Generator;

packages/react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@nrwl/workspace": "*",
4040
"@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
4141
"@svgr/webpack": "^5.5.0",
42+
"chalk": "4.1.0",
4243
"eslint-plugin-import": "^2.22.1",
4344
"eslint-plugin-jsx-a11y": "^6.4.1",
4445
"eslint-plugin-react": "^7.23.1",

packages/react/plugins/webpack.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import type { Configuration } from 'webpack';
2-
import * as ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin';
32

43
// Add React-specific configuration
54
function getWebpackConfig(config: Configuration) {
65
// TODO(jack): Remove in Nx 13
7-
const { isWebpack5 } = require('@nrwl/web/src/webpack/entry');
6+
const { ReactRefreshPlugin, isWebpack5 } = require('../src/webpack/entry');
87
config.module.rules.push(
98
{
109
test: /\.(png|jpe?g|gif|webp)$/,
@@ -55,12 +54,7 @@ function getWebpackConfig(config: Configuration) {
5554
}
5655
);
5756

58-
// TODO(jack): support webpack 5
59-
if (
60-
!isWebpack5 &&
61-
config.mode === 'development' &&
62-
config['devServer']?.hot
63-
) {
57+
if (config.mode === 'development' && config['devServer']?.hot) {
6458
// add `react-refresh/babel` to babel loader plugin
6559
const babelLoader = config.module.rules.find((rule) =>
6660
rule.loader.toString().includes('babel-loader')
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = function (useShim = true) {
2+
const webpack = require('webpack');
3+
webpack.webpack = webpack;
4+
5+
return {
6+
ReactRefreshPlugin: require('@pmmmwh/react-refresh-webpack-plugin'),
7+
webpack,
8+
};
9+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { logger, stripIndents } from '@nrwl/devkit';
2+
import chalk = require('chalk');
3+
4+
import { requireShim } from './require-shim';
5+
import packageJson = require('../../package.json');
6+
7+
function validateVersion(path) {
8+
if (
9+
packageJson.dependencies[path] ===
10+
requireShim(`${path}/package.json`).version
11+
) {
12+
logger.warn(`Found an outdated version of ${chalk.bold(path)}\n`);
13+
14+
logger.info(stripIndents`
15+
If you want to use webpack 5, try installing compatible versions of the plugins.
16+
See: https://nx.dev/guides/webpack-5
17+
`);
18+
19+
throw new Error('Incompatible version');
20+
}
21+
}
22+
23+
module.exports = function (onFallback) {
24+
try {
25+
validateVersion('@pmmmwh/react-refresh-webpack-plugin');
26+
} catch {
27+
logger.info(
28+
`NX Falling back to webpack 4 due to incompatible plugin versions`
29+
);
30+
onFallback();
31+
return require('./bundle4')();
32+
}
33+
34+
return {
35+
ReactRefreshPlugin: requireShim('@pmmmwh/react-refresh-webpack-plugin'),
36+
webpack: requireShim('webpack'),
37+
};
38+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO(jack): Delete for Nx 13
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { requireShim } from './require-shim';
2+
3+
const result = requireShim('webpack/package.json');
4+
const version = result?.version;
5+
6+
exports.default = undefined;
7+
8+
const forceWebpack4 = process.env.NX_FORCE_WEBPACK_4;
9+
10+
exports.isWebpack5 = !forceWebpack4 && /^5\./.test(version);
11+
12+
if (exports.isWebpack5) {
13+
Object.assign(
14+
exports,
15+
require('./bundle5')(() => {
16+
exports.isWebpack5 = false;
17+
})
18+
);
19+
} else {
20+
Object.assign(exports, require('./bundle4')());
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { appRootPath } from '@nrwl/tao/src/utils/app-root';
2+
import { join } from 'path';
3+
4+
export function requireShim(path: string) {
5+
try {
6+
return require(join(appRootPath, 'node_modules', path));
7+
} catch {
8+
return require(path);
9+
}
10+
}

packages/web/migrations.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,40 @@
3333
"description": "Update existing .babelrc files to add missing '@nrwl/web/babel' preset if necessary.",
3434
"factory": "./src/migrations/update-11-5-2/update-existing-babelrc-files"
3535
}
36+
},
37+
"packageJsonUpdates": {
38+
"12.6.3": {
39+
"version": "12.6.3-beta.1",
40+
"packages": {
41+
"mini-css-extract-plugin": {
42+
"version": "^2.1.0",
43+
"alwaysAddToPackageJson": false
44+
},
45+
"source-map-loader": {
46+
"version": "^3.0.0",
47+
"alwaysAddToPackageJson": false
48+
},
49+
"terser-webpack-plugin": {
50+
"version": "^5.1.1",
51+
"alwaysAddToPackageJson": false
52+
},
53+
"webpack-dev-server": {
54+
"version": "4.0.0-rc.0",
55+
"alwajysAddToPackageJson": false
56+
},
57+
"webpack-sources": {
58+
"version": "^3.0.2",
59+
"alwaysAddToPjackageJson": false
60+
},
61+
"react-refresh": {
62+
"version": "^0.10.0",
63+
"alwaysAddToPackageJson": false
64+
},
65+
"@pmmmwh/react-refresh-webpack-plugin": {
66+
"version": "0.5.0-rc.2",
67+
"alwaysAddToPackageJson": false
68+
}
69+
}
70+
}
3671
}
3772
}

0 commit comments

Comments
 (0)