diff --git a/tools/webpack/development.js b/tools/webpack/development.js index f7fd1f653b81e..4d2df930bd1d2 100644 --- a/tools/webpack/development.js +++ b/tools/webpack/development.js @@ -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]', @@ -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', + }, + externals: { + 'react-refresh/runtime': 'ReactRefreshRuntime', + }, + }; + + return [ runtimeConfig, entryConfig ]; }; diff --git a/webpack.config.js b/webpack.config.js index e04bd8d46e497..29ebbd696b875 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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;