Skip to content

Commit 6aed93b

Browse files
anomiextbradsha
andauthored
Update the monorepo to use React 19 (#53882)
We'll still need to maintain compatibility with React 18 until we drop support for WordPress Core versions that still supply it, but we can start developing against 19 now. To help people avoid letting their AIs break compatibility with React 18 once they see we're building against React 19, let's add CI test runs with React 18. We'll remove these when we drop support for WordPress Core versions that ship React 18. --------- Co-authored-by: tbradsha <32492176+tbradsha@users.noreply.github.com>
1 parent 1cb2ec4 commit 6aed93b

201 files changed

Lines changed: 4804 additions & 4495 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/files/generate-ci-matrix.php‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
// {string} A valid artifact name for any generated artifacts. If not given, will be derived from the name.
5252
'artifact' => null,
5353

54+
// (int|null) React version to test with.
55+
'with-react-version' => null,
56+
5457
// {bool} Whether to install WooCommerce.
5558
'with-woocommerce' => false,
5659

@@ -148,6 +151,16 @@
148151
);
149152
}
150153

154+
// Add JS React 18 test during the React 19 migration.
155+
// @todo Remove this when we drop support for WordPress Core versions containing React 18.
156+
$to_split[] = array(
157+
'name' => 'JS tests (React 18, %s)',
158+
'script' => 'test-js',
159+
'timeout' => 15, // 2026-10-06: Successful runs seem to take 3-7 minutes.
160+
'with-react-version' => 18,
161+
'split_config' => $js_split_config,
162+
);
163+
151164
/*
152165
* Split jobs: one job for each group in `split_config.groups`, then `split_config.generic` jobs to round-robin everything else.
153166
* Groups are keyed by job name, with a project slug or an array of slugs as the value.

‎.github/workflows/tests.yml‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
MONOREPO_BASE: ${{ github.workspace }}
5656
WITH_WOOCOMMERCE: ${{ matrix.with-woocommerce }}
5757
WITH_WPCOMSH: ${{ matrix.with-wpcomsh }}
58+
WITH_REACT_VERSION: ${{ matrix.with-react-version }}
5859
COVERAGE: ${{ matrix.coverage }}
5960
SPLIT_NUM: ${{ matrix.split-num }}
6061
SPLIT_TOTAL: ${{ matrix.split-total }}
@@ -93,9 +94,17 @@ jobs:
9394

9495
- name: Monorepo install
9596
run: |
96-
echo "::group::Pnpm"
97-
pnpm install
98-
echo "::endgroup::"
97+
if [[ "$WITH_REACT_VERSION" =~ ^[0-9]+$ ]]; then
98+
echo "Overriding React version to $WITH_REACT_VERSION"
99+
sed -i -e 's/^const withReactVersion = undefined;$/const withReactVersion = '"$WITH_REACT_VERSION"';/' .pnpmfile.cjs
100+
echo "::group::Pnpm update"
101+
pnpm update -r "react@^$WITH_REACT_VERSION" "react-dom@^$WITH_REACT_VERSION"
102+
echo "::endgroup::"
103+
else
104+
echo "::group::Pnpm"
105+
pnpm install
106+
echo "::endgroup::"
107+
fi
99108
100109
- name: Detect changed projects
101110
id: changed

‎.pnpmfile.cjs‎

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// CI hack. If you want to run tests against React 18 locally, you could set it to 18 and then run `pnpm dedupe` (just don't commit that change, or the resulting lockfile changes).
2+
const withReactVersion = undefined;
3+
14
/**
25
* Fix package dependencies.
36
*
@@ -135,7 +138,7 @@ async function fixDeps( pkg ) {
135138
// Currently @types/wordpress__block-editor is the only one still in use; see also https://github.com/WordPress/gutenberg/issues/67691
136139
if ( pkg.name.startsWith( '@types/wordpress__' ) && pkg.dependencies ) {
137140
for ( const k of Object.keys( pkg.dependencies ) ) {
138-
if ( k.startsWith( '@wordpress/' ) ) {
141+
if ( k.startsWith( '@wordpress/' ) || k === '@types/react' ) {
139142
pkg.dependencies[ k ] = '*';
140143
}
141144
}
@@ -180,6 +183,26 @@ async function fixDeps( pkg ) {
180183
* @return {object} Modified pkg.
181184
*/
182185
function fixPeerDeps( pkg ) {
186+
// Deps that still depend on React <19.
187+
const reactOldPkgs = new Set( [
188+
// Still on 18.
189+
'@woocommerce/email-editor', // direct dep from plugins/jetpack
190+
] );
191+
if ( reactOldPkgs.has( pkg.name ) ) {
192+
for ( const p of [ 'react', 'react-dom' ] ) {
193+
if ( ! pkg.peerDependencies?.[ p ] ) {
194+
continue;
195+
}
196+
197+
if (
198+
pkg.peerDependencies[ p ].match( /(?:^|\|\|\s*)(?:\^18|18\.x)/ ) &&
199+
! pkg.peerDependencies[ p ].match( /(?:^|\|\|\s*)(?:\^19|19\.x)/ )
200+
) {
201+
pkg.peerDependencies[ p ] += ' || ^19';
202+
}
203+
}
204+
}
205+
183206
// Outdated eslint deps.
184207
const eslintOldPkgs = new Set( [
185208
'eslint-plugin-import', // https://github.com/import-js/eslint-plugin-import/issues/3227
@@ -261,6 +284,22 @@ async function readPackage( pkg, context ) {
261284
if ( pkg.name ) {
262285
pkg = await fixDeps( pkg, context );
263286
pkg = fixPeerDeps( pkg, context );
287+
288+
// CI hack.
289+
if ( withReactVersion ) {
290+
for ( const field of [
291+
'dependencies',
292+
'devDependencies',
293+
'optionalDependencies',
294+
'peerDependencies',
295+
] ) {
296+
for ( const dep of [ 'react', 'react-dom' ] ) {
297+
if ( pkg[ field ]?.[ dep ] ) {
298+
pkg[ field ][ dep ] = '^' + withReactVersion;
299+
}
300+
}
301+
}
302+
}
264303
}
265304
return pkg;
266305
}
@@ -309,6 +348,20 @@ function afterAllResolved( lockfile ) {
309348
);
310349
}
311350

351+
// Now that we've moved to React 19, prevent React 18 from being re-installed.
352+
if (
353+
! withReactVersion &&
354+
( k.startsWith( 'react@18.' ) ||
355+
k.startsWith( 'react-dom@18.' ) ||
356+
k.startsWith( '@types/react@18.' ) ||
357+
k.startsWith( '@types/react-dom@18.' ) )
358+
) {
359+
throw new Error(
360+
// prettier-ignore
361+
`We've moved to React 19. Something you've done has tried to install \`${ k }\`, which would be likely to cause problems.`
362+
);
363+
}
364+
312365
// Forbid installing webpack without webpack-cli. It results in lots of spurious lockfile changes.
313366
// https://github.com/pnpm/pnpm/issues/3935
314367
if ( k.startsWith( 'webpack@' ) && ! v.optionalDependencies?.[ 'webpack-cli' ] ) {

0 commit comments

Comments
 (0)