Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 49 additions & 26 deletions tools/webpack/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,25 @@ const { baseDir } = require( './shared' );
* These scripts enable hot module replacement for plugins
* using `@wordpress/scripts` with the `--hot` flag.
*
* Returns two separate configs:
* 1. Runtime config - bundles react-refresh/runtime and exposes it as window.ReactRefreshRuntime
* 2. Entry config - uses the window global as an external to ensure both scripts share the same runtime instance
*
* @param {Object} env Environment options.
* @param {string} env.buildTarget Build target directory.
* @param {boolean} env.watch Whether to watch for changes.
* @return {Object} Webpack configuration object.
* @return {Object[]} Array of webpack configuration objects.
*/
module.exports = function( env = { buildTarget: 'src/', watch: false } ) {
const buildTarget = env.buildTarget || 'src/';

const entry = {
// React Refresh runtime - exposes ReactRefreshRuntime global.
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-runtime.js' ]: {
import: 'react-refresh/runtime',
library: {
name: 'ReactRefreshRuntime',
type: 'window',
},
},
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-runtime.min.js' ]: {
import: 'react-refresh/runtime',
library: {
name: 'ReactRefreshRuntime',
type: 'window',
},
},
// React Refresh entry - injects runtime into global hook before React loads.
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-entry.js' ]:
'@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js',
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-entry.min.js' ]:
'@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js',
};

return {
const baseConfig = {
target: 'browserslist',
// Must use development mode to preserve process.env.NODE_ENV checks
// in the source files. These scripts are only used during development.
mode: 'development',
devtool: false,
cache: true,
entry,
output: {
path: baseDir,
filename: '[name]',
Expand All @@ -69,4 +49,47 @@ module.exports = function( env = { buildTarget: 'src/', watch: false } ) {
},
watch: env.watch,
};

// Config for react-refresh-runtime.js - bundles the runtime and exposes
// it as window.ReactRefreshRuntime. No externals - this creates the global.
const runtimeConfig = {
...baseConfig,
name: 'runtime',
entry: {
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-runtime.js' ]: {
import: 'react-refresh/runtime',
library: {
name: 'ReactRefreshRuntime',
type: 'window',
},
},
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-runtime.min.js' ]: {
import: 'react-refresh/runtime',
library: {
name: 'ReactRefreshRuntime',
type: 'window',
},
},
},
};

// Config for react-refresh-entry.js - uses window.ReactRefreshRuntime as an
// external instead of bundling its own copy. This ensures the hooks set up
// by the entry are on the same runtime instance that plugins use for
// performReactRefresh().
const entryConfig = {
...baseConfig,
name: 'entry',
entry: {
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-entry.js' ]:
'@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js',
[ buildTarget + 'wp-includes/js/dist/development/react-refresh-entry.min.js' ]:
'@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js',
Comment on lines +84 to +87
Copy link
Member

Choose a reason for hiding this comment

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

TBH, this react refresh entry has nothing to do with Webpack. It works without that over-complication done by this plugin, increasing the bundle size needlessly.

We could explore replacing it directly with react-refresh which it depends upon anyway, just like in WordPress/gutenberg#74618

Copy link
Contributor Author

@youknowriad youknowriad Jan 20, 2026

Choose a reason for hiding this comment

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

@manzoorwanijk Please consider exploring that if you think it's important. The main goal is here is not to change behavior but to restore a working state. Do you think that solution would be something easy to implement without wp-scripts change.

},
externals: {
'react-refresh/runtime': 'ReactRefreshRuntime',
},
};

return [ runtimeConfig, entryConfig ];
};
3 changes: 2 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ module.exports = function (
// Only building Core-specific media files and development scripts.
// Blocks, packages, script modules, and vendors are now sourced from
// the Gutenberg build (see tools/gutenberg/copy-gutenberg-build.js).
// Note: developmentConfig returns an array of configs, so we spread it.
const config = [
mediaConfig( env ),
developmentConfig( env ),
...developmentConfig( env ),
];

return config;
Expand Down
Loading