diff --git a/lib/client-assets.php b/lib/client-assets.php index 62689eb9772190..db9d6af01680aa 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -369,6 +369,9 @@ function gutenberg_enqueue_stored_styles( $options = array() ) { function gutenberg_register_vendor_scripts( $scripts ) { $extension = SCRIPT_DEBUG ? '.js' : '.min.js'; + // Bust browser caches when the bundled React version changes (see build/constants.php, generated from package.json). + $react_vendor_version = defined( 'GUTENBERG_REACT_VENDOR_VERSION' ) ? GUTENBERG_REACT_VENDOR_VERSION : '18.3.1'; + gutenberg_override_script( $scripts, 'react', @@ -376,14 +379,14 @@ function gutenberg_register_vendor_scripts( $scripts ) { // WordPress Core in `wp_register_development_scripts` sets `wp-react-refresh-entry` as a dependency to `react` when `SCRIPT_DEBUG` is true. // We need to preserve that here. SCRIPT_DEBUG ? array( 'wp-react-refresh-entry', 'wp-polyfill' ) : array( 'wp-polyfill' ), - '18' + $react_vendor_version ); gutenberg_override_script( $scripts, 'react-dom', gutenberg_url( 'build/scripts/vendors/react-dom' . $extension ), array( 'react' ), - '18' + $react_vendor_version ); gutenberg_override_script( @@ -391,7 +394,7 @@ function gutenberg_register_vendor_scripts( $scripts ) { 'react-jsx-runtime', gutenberg_url( 'build/scripts/vendors/react-jsx-runtime' . $extension ), array( 'react' ), - '18' + $react_vendor_version ); } add_action( 'wp_default_scripts', 'gutenberg_register_vendor_scripts' ); diff --git a/lib/load.php b/lib/load.php index 88ef3408da2c90..53d128d2120eba 100644 --- a/lib/load.php +++ b/lib/load.php @@ -26,6 +26,10 @@ if ( file_exists( $constants_file ) && ! defined( 'GUTENBERG_VERSION' ) ) { $build_constants = require_once $constants_file; define( 'GUTENBERG_VERSION', $build_constants['version'] ); + $react_vendor_version = $build_constants['react_version'] ?? ''; + if ( $react_vendor_version && ! defined( 'GUTENBERG_REACT_VENDOR_VERSION' ) ) { + define( 'GUTENBERG_REACT_VENDOR_VERSION', $react_vendor_version ); + } } /** diff --git a/packages/wp-build/lib/php-generator.mjs b/packages/wp-build/lib/php-generator.mjs index 0a781cdffa9259..4da7cd7f7e7739 100644 --- a/packages/wp-build/lib/php-generator.mjs +++ b/packages/wp-build/lib/php-generator.mjs @@ -17,7 +17,7 @@ const __dirname = path.dirname( fileURLToPath( import.meta.url ) ); * * @param {string} rootDir Root directory path. * @param {string} baseUrlExpression PHP expression for base URL (e.g. "includes_url( 'build' )"). - * @return {Promise>} Replacements object with {{PREFIX}}, {{VERSION}}, {{BASE_URL}}. + * @return {Promise>} Replacements object with {{PREFIX}}, {{VERSION}}, {{BASE_URL}}, {{REACT_VERSION}}. */ export async function getPhpReplacements( rootDir, baseUrlExpression ) { const rootPackageJson = getPackageInfoFromFile( @@ -31,10 +31,17 @@ export async function getPhpReplacements( rootDir, baseUrlExpression ) { const name = rootPackageJson.wpPlugin?.name || 'gutenberg'; const version = rootPackageJson.version; + // Used to cache-bust vendor React bundles when the React pin changes independently of WordPress. + const reactVersion = + rootPackageJson.devDependencies?.react ?? + rootPackageJson.dependencies?.react ?? + ''; + return { '{{PREFIX}}': name, '{{VERSION}}': version, '{{BASE_URL}}': baseUrlExpression, + '{{REACT_VERSION}}': reactVersion, }; } diff --git a/packages/wp-build/templates/constants.php.template b/packages/wp-build/templates/constants.php.template index 8136230c9f499a..018ce317f508a5 100644 --- a/packages/wp-build/templates/constants.php.template +++ b/packages/wp-build/templates/constants.php.template @@ -11,4 +11,5 @@ return array( 'version' => '{{VERSION}}', 'build_url' => {{BASE_URL}}, + 'react_version' => '{{REACT_VERSION}}', );