diff --git a/package-lock.json b/package-lock.json index dcb77642f20886..3ec37cc79dc451 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15000,6 +15000,10 @@ "resolved": "widgets/quick-draft", "link": true }, + "node_modules/@wordpress/react-18": { + "resolved": "tools/react-18", + "link": true + }, "node_modules/@wordpress/react-19": { "resolved": "tools/react-19", "link": true @@ -49028,6 +49032,7 @@ "@typescript/native": "npm:typescript@^7.0.2", "@wordpress/babel-preset-default": "file:../../packages/babel-preset-default", "@wordpress/build": "file:../../packages/wp-build", + "@wordpress/react-18": "file:../react-18", "@wordpress/react-19": "file:../react-19", "@wordpress/scripts": "file:../../packages/scripts", "babel-plugin-inline-json-import": "^0.3.2", @@ -49036,8 +49041,6 @@ "esbuild": "^0.27.2", "fast-glob": "^3.2.7", "magic-string": "^0.30.21", - "react": "^18.3.1", - "react-dom": "^18.3.1", "rimraf": "^5.0.10", "source-map": "^0.6.1" } @@ -49088,6 +49091,15 @@ "typescript-eslint": "^8.57.1" } }, + "tools/react-18": { + "name": "@wordpress/react-18", + "version": "0.0.0", + "license": "GPL-2.0-or-later", + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + } + }, "tools/react-19": { "name": "@wordpress/react-19", "version": "0.0.0", diff --git a/syncpack.config.mjs b/syncpack.config.mjs index 58d2f4631a4142..13f867c48ca595 100644 --- a/syncpack.config.mjs +++ b/syncpack.config.mjs @@ -19,9 +19,9 @@ export default { policy: 'sameRange', }, { - label: '`@wordpress/react-19` is the experimental React 19 build; it intentionally declares React v19.', + label: '`@wordpress/react-18` and `@wordpress/react-19` are standalone React builds pinned to their respective major versions, independent of the repo-wide React version.', dependencies: [ 'react', 'react-dom' ], - packages: [ '@wordpress/react-19' ], + packages: [ '@wordpress/react-18', '@wordpress/react-19' ], isIgnored: true, }, { diff --git a/tools/build-scripts/package.json b/tools/build-scripts/package.json index ba1a15b63b51b7..215d6832ff562f 100644 --- a/tools/build-scripts/package.json +++ b/tools/build-scripts/package.json @@ -27,6 +27,7 @@ "@typescript/native": "npm:typescript@^7.0.2", "@wordpress/babel-preset-default": "file:../../packages/babel-preset-default", "@wordpress/build": "file:../../packages/wp-build", + "@wordpress/react-18": "file:../react-18", "@wordpress/react-19": "file:../react-19", "@wordpress/scripts": "file:../../packages/scripts", "babel-plugin-inline-json-import": "^0.3.2", @@ -35,8 +36,6 @@ "esbuild": "^0.27.2", "fast-glob": "^3.2.7", "magic-string": "^0.30.21", - "react": "^18.3.1", - "react-dom": "^18.3.1", "rimraf": "^5.0.10", "source-map": "^0.6.1" }, diff --git a/tools/build-scripts/packages/build-vendors.mjs b/tools/build-scripts/packages/build-vendors.mjs index d0710a6a0d9bd8..cdcc80dd967409 100644 --- a/tools/build-scripts/packages/build-vendors.mjs +++ b/tools/build-scripts/packages/build-vendors.mjs @@ -31,27 +31,21 @@ const patchReactDOM = createPatcher( const VENDOR_SCRIPTS = [ { - name: 'react', + name: '@wordpress/react-18/react', global: 'React', handle: 'react', dependencies: [ 'wp-polyfill' ], version: '18.3.1', }, { - name: 'react-dom', + name: '@wordpress/react-18/react-dom', global: 'ReactDOM', handle: 'react-dom', dependencies: [ 'react' ], - contents: [ - 'module.exports = {', - ' ...require("react-dom"),', - ' ...require("react-dom/client"),', - '};', - ].join( '\n' ), version: '18.3.1', }, { - name: 'react/jsx-runtime', + name: '@wordpress/react-18/react-jsx-runtime', global: 'ReactJSXRuntime', handle: 'react-jsx-runtime', dependencies: [ 'react' ], @@ -128,7 +122,7 @@ async function patchVendorOutput( fileName, patch ) { * This is used to build packages like React that don't ship UMD builds. * * @param {Object} config Vendor script configuration. - * @param {string} config.name Package name (e.g., 'react', 'react-dom', 'react/jsx-runtime'). + * @param {string} config.name Entry point name (e.g., '@wordpress/react-18/react'). * @param {string} config.global Global variable name (e.g., 'React', 'ReactDOM'). * @param {string} config.handle WordPress script handle (e.g., 'react', 'react-dom'). * @param {string[]} config.dependencies WordPress script dependencies. @@ -136,7 +130,7 @@ async function patchVendorOutput( fileName, patch ) { * @return {Promise} Promise that resolves when all builds are finished. */ async function bundleVendorScript( config ) { - const { name, global, handle, contents, dependencies } = config; + const { name, global, handle, dependencies } = config; // Plugin that externalizes the `react` package. const reactExternalPlugin = { @@ -168,6 +162,7 @@ async function bundleVendorScript( config ) { globalName: global, target: 'esnext', platform: 'browser', + entryPoints: [ name ], // Resolve imports from this workspace's dependencies. absWorkingDir: WORKSPACE_DIR, plugins: dependencies?.includes( 'react' ) @@ -175,16 +170,6 @@ async function bundleVendorScript( config ) { : [], }; - if ( contents ) { - esbuildOptions.stdin = { - contents, - resolveDir: WORKSPACE_DIR, - loader: 'js', - }; - } else { - esbuildOptions.entryPoints = [ name ]; - } - await Promise.all( [ esbuild.build( { ...esbuildOptions, diff --git a/tools/react-18/package.json b/tools/react-18/package.json new file mode 100644 index 00000000000000..05980d67a11848 --- /dev/null +++ b/tools/react-18/package.json @@ -0,0 +1,34 @@ +{ + "name": "@wordpress/react-18", + "version": "0.0.0", + "description": "React 18 for Gutenberg.", + "private": true, + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "gutenberg", + "react" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/tools/react-18", + "repository": { + "type": "git", + "url": "git+https://github.com/WordPress/gutenberg.git", + "directory": "tools/react-18" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "exports": { + "./react": "./react.js", + "./react-dom": "./react-dom.js", + "./react-jsx-runtime": "./react-jsx-runtime.js" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/tools/react-18/react-dom.js b/tools/react-18/react-dom.js new file mode 100644 index 00000000000000..42c5fd1f836eb8 --- /dev/null +++ b/tools/react-18/react-dom.js @@ -0,0 +1,4 @@ +module.exports = { + ...require( 'react-dom' ), + ...require( 'react-dom/client' ), +}; diff --git a/tools/react-18/react-jsx-runtime.js b/tools/react-18/react-jsx-runtime.js new file mode 100644 index 00000000000000..8a6ae2123fa1ce --- /dev/null +++ b/tools/react-18/react-jsx-runtime.js @@ -0,0 +1 @@ +module.exports = require( 'react/jsx-runtime' ); diff --git a/tools/react-18/react.js b/tools/react-18/react.js new file mode 100644 index 00000000000000..7c2b3e225d9ceb --- /dev/null +++ b/tools/react-18/react.js @@ -0,0 +1 @@ +module.exports = require( 'react' );