diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js new file mode 100644 index 0000000000000..53058d0d18841 --- /dev/null +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export * from 'react-client/src/ReactFlightClientConfigBrowser'; +export * from 'react-server-dom-esm/src/ReactFlightClientConfigESMBundler'; +export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM'; +export const usedWithSSR = false; diff --git a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js index a4a68b8773170..8390c4c06b439 100644 --- a/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js +++ b/packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js @@ -7,7 +7,6 @@ * @flow */ -// This should really have a Node and a Browser fork but to avoid too many configs we limit this to build the same for both export * from 'react-client/src/ReactFlightClientConfigBrowser'; export * from 'react-server-dom-esm/src/ReactFlightClientConfigESMBundler'; export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM'; diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 1eb339328d940..72d037bbafea6 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -14,7 +14,7 @@ import type {RootTag} from './ReactRootTags'; import type {WorkTag} from './ReactWorkTags'; import type {TypeOfMode} from './ReactTypeOfMode'; import type {Lanes} from './ReactFiberLane'; -import type {SuspenseInstance} from './ReactFiberConfig'; +import type {SuspenseInstance} from 'react-reconciler/src/ReactFiberConfig'; import type { OffscreenProps, OffscreenInstance, diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-bun.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom-bun.js deleted file mode 100644 index 4932b1a787bb9..0000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-bun.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-edge-webpack.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom-edge-webpack.js deleted file mode 100644 index 4932b1a787bb9..0000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-edge-webpack.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-fb.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom-fb.js deleted file mode 100644 index 4932b1a787bb9..0000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-fb.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-legacy.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom-legacy.js deleted file mode 100644 index 4932b1a787bb9..0000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-legacy.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node-esm.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node-esm.js deleted file mode 100644 index 4932b1a787bb9..0000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node-esm.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node-webpack.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node-webpack.js deleted file mode 100644 index 4932b1a787bb9..0000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node-webpack.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node.js deleted file mode 100644 index 4932b1a787bb9..0000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-node.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM'; diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.dom-browser.js b/packages/react-reconciler/src/forks/ReactFiberConfig.dom.js similarity index 100% rename from packages/react-reconciler/src/forks/ReactFiberConfig.dom-browser.js rename to packages/react-reconciler/src/forks/ReactFiberConfig.dom.js diff --git a/packages/react-server/src/forks/ReactFizzConfig.dom-bun.js b/packages/react-server/src/forks/ReactFizzConfig.dom-bun.js deleted file mode 100644 index 5f887770d211e..0000000000000 --- a/packages/react-server/src/forks/ReactFizzConfig.dom-bun.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ -import type {Request} from 'react-server/src/ReactFizzServer'; - -export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM'; - -export const supportsRequestStorage = false; -export const requestStorage: AsyncLocalStorage = (null: any); diff --git a/packages/react-server/src/forks/ReactFizzConfig.dom-edge-webpack.js b/packages/react-server/src/forks/ReactFizzConfig.dom-edge.js similarity index 100% rename from packages/react-server/src/forks/ReactFizzConfig.dom-edge-webpack.js rename to packages/react-server/src/forks/ReactFizzConfig.dom-edge.js diff --git a/packages/react-server/src/forks/ReactFizzConfig.dom-fb.js b/packages/react-server/src/forks/ReactFizzConfig.dom-fb.js deleted file mode 100644 index 5f887770d211e..0000000000000 --- a/packages/react-server/src/forks/ReactFizzConfig.dom-fb.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ -import type {Request} from 'react-server/src/ReactFizzServer'; - -export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM'; - -export const supportsRequestStorage = false; -export const requestStorage: AsyncLocalStorage = (null: any); diff --git a/packages/react-server/src/forks/ReactFizzConfig.dom-node-esm.js b/packages/react-server/src/forks/ReactFizzConfig.dom-node-esm.js deleted file mode 100644 index 71c6ab5a5586c..0000000000000 --- a/packages/react-server/src/forks/ReactFizzConfig.dom-node-esm.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ -import {AsyncLocalStorage} from 'async_hooks'; - -import type {Request} from 'react-server/src/ReactFizzServer'; - -export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM'; - -export const supportsRequestStorage = true; -export const requestStorage: AsyncLocalStorage = - new AsyncLocalStorage(); diff --git a/packages/react-server/src/forks/ReactFizzConfig.dom-node-webpack.js b/packages/react-server/src/forks/ReactFizzConfig.dom-node-webpack.js deleted file mode 100644 index 71c6ab5a5586c..0000000000000 --- a/packages/react-server/src/forks/ReactFizzConfig.dom-node-webpack.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ -import {AsyncLocalStorage} from 'async_hooks'; - -import type {Request} from 'react-server/src/ReactFizzServer'; - -export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM'; - -export const supportsRequestStorage = true; -export const requestStorage: AsyncLocalStorage = - new AsyncLocalStorage(); diff --git a/packages/react-server/src/forks/ReactFizzConfig.dom-browser.js b/packages/react-server/src/forks/ReactFizzConfig.dom.js similarity index 100% rename from packages/react-server/src/forks/ReactFizzConfig.dom-browser.js rename to packages/react-server/src/forks/ReactFizzConfig.dom.js diff --git a/packages/react-server/src/forks/ReactFlightServerConfig.dom-node-webpack.js b/packages/react-server/src/forks/ReactFlightServerConfig.dom-browser-esm.js similarity index 86% rename from packages/react-server/src/forks/ReactFlightServerConfig.dom-node-webpack.js rename to packages/react-server/src/forks/ReactFlightServerConfig.dom-browser-esm.js index 10d36f14a93eb..d52bf8ad7451e 100644 --- a/packages/react-server/src/forks/ReactFlightServerConfig.dom-node-webpack.js +++ b/packages/react-server/src/forks/ReactFlightServerConfig.dom-browser-esm.js @@ -10,7 +10,7 @@ import {AsyncLocalStorage} from 'async_hooks'; import type {Request} from 'react-server/src/ReactFlightServer'; -export * from 'react-server-dom-webpack/src/ReactFlightServerConfigWebpackBundler'; +export * from 'react-server-dom-esm/src/ReactFlightServerConfigESMBundler'; export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM'; export const supportsRequestStorage = true; diff --git a/packages/react-server/src/forks/ReactFlightServerConfig.dom-edge-webpack.js b/packages/react-server/src/forks/ReactFlightServerConfig.dom-edge.js similarity index 100% rename from packages/react-server/src/forks/ReactFlightServerConfig.dom-edge-webpack.js rename to packages/react-server/src/forks/ReactFlightServerConfig.dom-edge.js diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.dom-edge-webpack.js b/packages/react-server/src/forks/ReactServerStreamConfig.dom-edge.js similarity index 100% rename from packages/react-server/src/forks/ReactServerStreamConfig.dom-edge-webpack.js rename to packages/react-server/src/forks/ReactServerStreamConfig.dom-edge.js diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.dom-node-esm.js b/packages/react-server/src/forks/ReactServerStreamConfig.dom-node-esm.js deleted file mode 100644 index 1a3871aef2e8b..0000000000000 --- a/packages/react-server/src/forks/ReactServerStreamConfig.dom-node-esm.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from '../ReactServerStreamConfigNode'; diff --git a/packages/react-server/src/forks/ReactServerStreamConfig.dom-node-webpack.js b/packages/react-server/src/forks/ReactServerStreamConfig.dom-node-webpack.js deleted file mode 100644 index 1a3871aef2e8b..0000000000000 --- a/packages/react-server/src/forks/ReactServerStreamConfig.dom-node-webpack.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -export * from '../ReactServerStreamConfigNode'; diff --git a/scripts/flow/createFlowConfigs.js b/scripts/flow/createFlowConfigs.js index 8a7c22b6d337c..cb4a89b5d95e3 100644 --- a/scripts/flow/createFlowConfigs.js +++ b/scripts/flow/createFlowConfigs.js @@ -9,6 +9,7 @@ const chalk = require('chalk'); const fs = require('fs'); +const path = require('path'); const mkdirp = require('mkdirp'); const inlinedHostConfigs = require('../shared/inlinedHostConfigs'); @@ -16,6 +17,40 @@ const configTemplate = fs .readFileSync(__dirname + '/config/flowconfig') .toString(); +// stores all forks discovered during config generation +const allForks = new Set(); +// maps forked file to the base path containing it and it's forks (it's parent) +const forkedFiles = new Map(); + +function findForks(file) { + const basePath = path.join(file, '..'); + const forksPath = path.join(basePath, 'forks'); + const forks = fs.readdirSync(path.join('packages', forksPath)); + forks.forEach(f => allForks.add('forks/' + f)); + forkedFiles.set(file, basePath); + return basePath; +} + +function addFork(forks, renderer, file) { + let basePath = forkedFiles.get(file); + if (!basePath) { + basePath = findForks(file); + } + + const baseFilename = file.slice(basePath.length + 1); + + const parts = renderer.split('-'); + while (parts.length) { + const candidate = `forks/${baseFilename}.${parts.join('-')}.js`; + if (allForks.has(candidate)) { + forks.set(candidate, `${baseFilename}$$`); + return; + } + parts.pop(); + } + throw new Error(`Cannot find fork for ${file} for renderer ${renderer}`); +} + function writeConfig( renderer, rendererInfo, @@ -44,34 +79,39 @@ function writeConfig( } ignoredPaths.push(`.*/packages/${otherPath}`); }); + }); + + const forks = new Map(); + addFork(forks, renderer, 'react-reconciler/src/ReactFiberConfig'); + addFork(forks, serverRenderer, 'react-server/src/ReactServerStreamConfig'); + addFork(forks, serverRenderer, 'react-server/src/ReactFizzConfig'); + addFork(forks, flightRenderer, 'react-server/src/ReactFlightServerConfig'); + addFork(forks, flightRenderer, 'react-client/src/ReactFlightClientConfig'); + forks.set( + 'react-devtools-shared/src/config/DevToolsFeatureFlags.default', + 'react-devtools-feature-flags', + ); - if ( - otherRenderer.shortName !== serverRenderer && - otherRenderer.shortName !== flightRenderer - ) { - ignoredPaths.push( - `.*/packages/.*/forks/.*\\.${otherRenderer.shortName}.js`, - ); + allForks.forEach(fork => { + if (!forks.has(fork)) { + ignoredPaths.push(`.*/packages/.*/${fork}`); } }); + let moduleMappings = ''; + forks.forEach((source, target) => { + moduleMappings += `module.name_mapper='${source.slice( + source.lastIndexOf('/') + 1, + )}' -> '${target}'\n`; + }); + const config = configTemplate .replace( '%CI_MAX_WORKERS%\n', // On CI, we seem to need to limit workers. process.env.CI ? 'server.max_workers=4\n' : '', ) - .replace( - '%REACT_RENDERER_FLOW_OPTIONS%', - ` -module.name_mapper='ReactFiberConfig$$' -> 'forks/ReactFiberConfig.${renderer}' -module.name_mapper='ReactServerStreamConfig$$' -> 'forks/ReactServerStreamConfig.${serverRenderer}' -module.name_mapper='ReactFizzConfig$$' -> 'forks/ReactFizzConfig.${serverRenderer}' -module.name_mapper='ReactFlightServerConfig$$' -> 'forks/ReactFlightServerConfig.${flightRenderer}' -module.name_mapper='ReactFlightClientConfig$$' -> 'forks/ReactFlightClientConfig.${flightRenderer}' -module.name_mapper='react-devtools-feature-flags' -> 'react-devtools-shared/src/config/DevToolsFeatureFlags.default' - `.trim(), - ) + .replace('%REACT_RENDERER_FLOW_OPTIONS%', moduleMappings.trim()) .replace('%REACT_RENDERER_FLOW_IGNORES%', ignoredPaths.join('\n')); const disclaimer = ` diff --git a/scripts/jest/setupHostConfigs.js b/scripts/jest/setupHostConfigs.js index 85fe96cd2ac06..5ea6eb0f5810d 100644 --- a/scripts/jest/setupHostConfigs.js +++ b/scripts/jest/setupHostConfigs.js @@ -1,6 +1,7 @@ 'use strict'; const fs = require('fs'); +const nodePath = require('path'); const inlinedHostConfigs = require('../shared/inlinedHostConfigs'); function resolveEntryFork(resolvedEntry, isFBBundle) { @@ -117,7 +118,20 @@ function mockAllConfigs(rendererInfo) { jest.mock(path, () => { let idx = path.lastIndexOf('/'); let forkPath = path.slice(0, idx) + '/forks' + path.slice(idx); - return jest.requireActual(`${forkPath}.${rendererInfo.shortName}.js`); + let parts = rendererInfo.shortName.split('-'); + while (parts.length) { + try { + const candidate = `${forkPath}.${parts.join('-')}.js`; + fs.statSync(nodePath.join(process.cwd(), 'packages', candidate)); + return jest.requireActual(candidate); + } catch (error) { + // try without a part + } + parts.pop(); + } + throw new Error( + `Expected to find a fork for ${path} but did not find one.` + ); }); }); } diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 6e6384aba66f8..fe0158a9a477d 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -341,153 +341,162 @@ function getPlugins( pureExternalModules, bundle ) { - const forks = Modules.getForks(bundleType, entry, moduleType, bundle); - const isProduction = isProductionBundleType(bundleType); - const isProfiling = isProfilingBundleType(bundleType); - const isUMDBundle = - bundleType === UMD_DEV || - bundleType === UMD_PROD || - bundleType === UMD_PROFILING; - const isFBWWWBundle = - bundleType === FB_WWW_DEV || - bundleType === FB_WWW_PROD || - bundleType === FB_WWW_PROFILING; - const isRNBundle = - bundleType === RN_OSS_DEV || - bundleType === RN_OSS_PROD || - bundleType === RN_OSS_PROFILING || - bundleType === RN_FB_DEV || - bundleType === RN_FB_PROD || - bundleType === RN_FB_PROFILING; - const shouldStayReadable = isFBWWWBundle || isRNBundle || forcePrettyOutput; - return [ - // Keep dynamic imports as externals - dynamicImports(), - { - name: 'rollup-plugin-flow-remove-types', - transform(code) { - const transformed = flowRemoveTypes(code); - return { - code: transformed.toString(), - map: transformed.generateMap(), - }; - }, - }, - // Shim any modules that need forking in this environment. - useForks(forks), - // Ensure we don't try to bundle any fbjs modules. - forbidFBJSImports(), - // Use Node resolution mechanism. - resolve({ - // skip: externals, // TODO: options.skip was removed in @rollup/plugin-node-resolve 3.0.0 - }), - // Remove license headers from individual modules - stripBanner({ - exclude: 'node_modules/**/*', - }), - // Compile to ES2015. - babel( - getBabelConfig( - updateBabelOptions, - bundleType, - packageName, - externals, - !isProduction, - bundle - ) - ), - // Remove 'use strict' from individual source files. - { - transform(source) { - return source.replace(/['"]use strict["']/g, ''); - }, - }, - // Turn __DEV__ and process.env checks into constants. - replace({ - preventAssignment: true, - values: { - __DEV__: isProduction ? 'false' : 'true', - __PROFILE__: isProfiling || !isProduction ? 'true' : 'false', - __UMD__: isUMDBundle ? 'true' : 'false', - 'process.env.NODE_ENV': isProduction ? "'production'" : "'development'", - __EXPERIMENTAL__, + try { + const forks = Modules.getForks(bundleType, entry, moduleType, bundle); + const isProduction = isProductionBundleType(bundleType); + const isProfiling = isProfilingBundleType(bundleType); + const isUMDBundle = + bundleType === UMD_DEV || + bundleType === UMD_PROD || + bundleType === UMD_PROFILING; + const isFBWWWBundle = + bundleType === FB_WWW_DEV || + bundleType === FB_WWW_PROD || + bundleType === FB_WWW_PROFILING; + const isRNBundle = + bundleType === RN_OSS_DEV || + bundleType === RN_OSS_PROD || + bundleType === RN_OSS_PROFILING || + bundleType === RN_FB_DEV || + bundleType === RN_FB_PROD || + bundleType === RN_FB_PROFILING; + const shouldStayReadable = isFBWWWBundle || isRNBundle || forcePrettyOutput; + return [ + // Keep dynamic imports as externals + dynamicImports(), + { + name: 'rollup-plugin-flow-remove-types', + transform(code) { + const transformed = flowRemoveTypes(code); + return { + code: transformed.toString(), + map: transformed.generateMap(), + }; + }, }, - }), - // The CommonJS plugin *only* exists to pull "art" into "react-art". - // I'm going to port "art" to ES modules to avoid this problem. - // Please don't enable this for anything else! - isUMDBundle && entry === 'react-art' && commonjs(), - // Apply dead code elimination and/or minification. - // closure doesn't yet support leaving ESM imports intact - isProduction && - bundleType !== ESM_PROD && - closure({ - compilation_level: 'SIMPLE', - language_in: 'ECMASCRIPT_2020', - language_out: - bundleType === NODE_ES2015 - ? 'ECMASCRIPT_2020' - : bundleType === BROWSER_SCRIPT - ? 'ECMASCRIPT5' - : 'ECMASCRIPT5_STRICT', - emit_use_strict: - bundleType !== BROWSER_SCRIPT && - bundleType !== ESM_PROD && - bundleType !== ESM_DEV, - env: 'CUSTOM', - warning_level: 'QUIET', - apply_input_source_maps: false, - use_types_for_optimization: false, - process_common_js_modules: false, - rewrite_polyfills: false, - inject_libraries: false, - allow_dynamic_import: true, - - // Don't let it create global variables in the browser. - // https://github.com/facebook/react/issues/10909 - assume_function_wrapper: !isUMDBundle, - renaming: !shouldStayReadable, + // Shim any modules that need forking in this environment. + useForks(forks), + // Ensure we don't try to bundle any fbjs modules. + forbidFBJSImports(), + // Use Node resolution mechanism. + resolve({ + // skip: externals, // TODO: options.skip was removed in @rollup/plugin-node-resolve 3.0.0 }), - // Add the whitespace back if necessary. - shouldStayReadable && - prettier({ - parser: 'flow', - singleQuote: false, - trailingComma: 'none', - bracketSpacing: true, + // Remove license headers from individual modules + stripBanner({ + exclude: 'node_modules/**/*', }), - // License and haste headers, top-level `if` blocks. - { - renderChunk(source) { - return Wrappers.wrapBundle( - source, - bundleType, - globalName, - filename, - moduleType, - bundle.wrapWithModuleBoundaries - ); - }, - }, - // Record bundle size. - sizes({ - getSize: (size, gzip) => { - const currentSizes = Stats.currentBuildResults.bundleSizes; - const recordIndex = currentSizes.findIndex( - record => - record.filename === filename && record.bundleType === bundleType - ); - const index = recordIndex !== -1 ? recordIndex : currentSizes.length; - currentSizes[index] = { - filename, + // Compile to ES2015. + babel( + getBabelConfig( + updateBabelOptions, bundleType, packageName, - size, - gzip, - }; + externals, + !isProduction, + bundle + ) + ), + // Remove 'use strict' from individual source files. + { + transform(source) { + return source.replace(/['"]use strict["']/g, ''); + }, + }, + // Turn __DEV__ and process.env checks into constants. + replace({ + preventAssignment: true, + values: { + __DEV__: isProduction ? 'false' : 'true', + __PROFILE__: isProfiling || !isProduction ? 'true' : 'false', + __UMD__: isUMDBundle ? 'true' : 'false', + 'process.env.NODE_ENV': isProduction + ? "'production'" + : "'development'", + __EXPERIMENTAL__, + }, + }), + // The CommonJS plugin *only* exists to pull "art" into "react-art". + // I'm going to port "art" to ES modules to avoid this problem. + // Please don't enable this for anything else! + isUMDBundle && entry === 'react-art' && commonjs(), + // Apply dead code elimination and/or minification. + // closure doesn't yet support leaving ESM imports intact + isProduction && + bundleType !== ESM_PROD && + closure({ + compilation_level: 'SIMPLE', + language_in: 'ECMASCRIPT_2020', + language_out: + bundleType === NODE_ES2015 + ? 'ECMASCRIPT_2020' + : bundleType === BROWSER_SCRIPT + ? 'ECMASCRIPT5' + : 'ECMASCRIPT5_STRICT', + emit_use_strict: + bundleType !== BROWSER_SCRIPT && + bundleType !== ESM_PROD && + bundleType !== ESM_DEV, + env: 'CUSTOM', + warning_level: 'QUIET', + apply_input_source_maps: false, + use_types_for_optimization: false, + process_common_js_modules: false, + rewrite_polyfills: false, + inject_libraries: false, + allow_dynamic_import: true, + + // Don't let it create global variables in the browser. + // https://github.com/facebook/react/issues/10909 + assume_function_wrapper: !isUMDBundle, + renaming: !shouldStayReadable, + }), + // Add the whitespace back if necessary. + shouldStayReadable && + prettier({ + parser: 'flow', + singleQuote: false, + trailingComma: 'none', + bracketSpacing: true, + }), + // License and haste headers, top-level `if` blocks. + { + renderChunk(source) { + return Wrappers.wrapBundle( + source, + bundleType, + globalName, + filename, + moduleType, + bundle.wrapWithModuleBoundaries + ); + }, }, - }), - ].filter(Boolean); + // Record bundle size. + sizes({ + getSize: (size, gzip) => { + const currentSizes = Stats.currentBuildResults.bundleSizes; + const recordIndex = currentSizes.findIndex( + record => + record.filename === filename && record.bundleType === bundleType + ); + const index = recordIndex !== -1 ? recordIndex : currentSizes.length; + currentSizes[index] = { + filename, + bundleType, + packageName, + size, + gzip, + }; + }, + }), + ].filter(Boolean); + } catch (error) { + console.error( + chalk.red(`There was an error preparing plugins for entry "${entry}"`) + ); + throw error; + } } function shouldSkipBundle(bundle, bundleType) { diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index d51e784f68378..d8e103d5f1470 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -1,5 +1,6 @@ 'use strict'; +const fs = require('node:fs'); const {bundleTypes, moduleTypes} = require('./bundles'); const inlinedHostConfigs = require('../shared/inlinedHostConfigs'); @@ -28,6 +29,22 @@ const __EXPERIMENTAL__ = ? RELEASE_CHANNEL === 'experimental' : true; +function findNearestExistingForkFile(path, segmentedIdentifier, suffix) { + const segments = segmentedIdentifier.split('-'); + while (segments.length) { + const candidate = segments.join('-'); + const forkPath = path + candidate + suffix; + try { + fs.statSync(forkPath); + return forkPath; + } catch (error) { + // Try the next candidate. + } + segments.pop(); + } + return null; +} + // If you need to replace a file with another file for a specific environment, // add it to this list with the logic for choosing the right replacement. @@ -261,7 +278,16 @@ const forks = Object.freeze({ // eslint-disable-next-line no-for-of-loops/no-for-of-loops for (let rendererInfo of inlinedHostConfigs) { if (rendererInfo.entryPoints.indexOf(entry) !== -1) { - return `./packages/react-reconciler/src/forks/ReactFiberConfig.${rendererInfo.shortName}.js`; + const foundFork = findNearestExistingForkFile( + './packages/react-reconciler/src/forks/ReactFiberConfig.', + rendererInfo.shortName, + '.js' + ); + if (foundFork) { + return foundFork; + } + // fall through to error + break; } } throw new Error( @@ -289,7 +315,16 @@ const forks = Object.freeze({ if (!rendererInfo.isServerSupported) { return null; } - return `./packages/react-server/src/forks/ReactServerStreamConfig.${rendererInfo.shortName}.js`; + const foundFork = findNearestExistingForkFile( + './packages/react-server/src/forks/ReactServerStreamConfig.', + rendererInfo.shortName, + '.js' + ); + if (foundFork) { + return foundFork; + } + // fall through to error + break; } } throw new Error( @@ -317,7 +352,16 @@ const forks = Object.freeze({ if (!rendererInfo.isServerSupported) { return null; } - return `./packages/react-server/src/forks/ReactFizzConfig.${rendererInfo.shortName}.js`; + const foundFork = findNearestExistingForkFile( + './packages/react-server/src/forks/ReactFizzConfig.', + rendererInfo.shortName, + '.js' + ); + if (foundFork) { + return foundFork; + } + // fall through to error + break; } } throw new Error( @@ -345,7 +389,23 @@ const forks = Object.freeze({ if (!rendererInfo.isServerSupported) { return null; } - return `./packages/react-server/src/forks/ReactFlightServerConfig.${rendererInfo.shortName}.js`; + if (rendererInfo.isFlightSupported !== true) { + return new Error( + `Expected not to use ReactFlightServerConfig with "${entry}" entry point ` + + 'in ./scripts/shared/inlinedHostConfigs.js. Update the renderer config to ' + + 'activate flight suppport and add a matching fork implementation for ReactFlightServerConfig.' + ); + } + const foundFork = findNearestExistingForkFile( + './packages/react-server/src/forks/ReactFlightServerConfig.', + rendererInfo.shortName, + '.js' + ); + if (foundFork) { + return foundFork; + } + // fall through to error + break; } } throw new Error( @@ -373,7 +433,23 @@ const forks = Object.freeze({ if (!rendererInfo.isServerSupported) { return null; } - return `./packages/react-client/src/forks/ReactFlightClientConfig.${rendererInfo.shortName}.js`; + if (rendererInfo.isFlightSupported !== true) { + return new Error( + `Expected not to use ReactFlightClientConfig with "${entry}" entry point ` + + 'in ./scripts/shared/inlinedHostConfigs.js. Update the renderer config to ' + + 'activate flight suppport and add a matching fork implementation for ReactFlightClientConfig.' + ); + } + const foundFork = findNearestExistingForkFile( + './packages/react-client/src/forks/ReactFlightClientConfig.', + rendererInfo.shortName, + '.js' + ); + if (foundFork) { + return foundFork; + } + // fall through to error + break; } } throw new Error( diff --git a/scripts/shared/inlinedHostConfigs.js b/scripts/shared/inlinedHostConfigs.js index 4b35976aeba29..a6c1772f11c72 100644 --- a/scripts/shared/inlinedHostConfigs.js +++ b/scripts/shared/inlinedHostConfigs.js @@ -93,6 +93,28 @@ module.exports = [ isFlowTyped: true, isServerSupported: true, }, + { + shortName: 'dom-browser-esm', + entryPoints: ['react-server-dom-esm/client.browser'], + paths: [ + 'react-dom', + 'react-dom/client', + 'react-dom/server', + 'react-dom/server.node', + 'react-dom-bindings', + 'react-server-dom-esm', + 'react-server-dom-esm/client', + 'react-server-dom-esm/client.browser', + 'react-devtools', + 'react-devtools-core', + 'react-devtools-shell', + 'react-devtools-shared', + 'react-interactions', + 'shared/ReactDOMSharedInternals', + ], + isFlowTyped: true, + isServerSupported: true, + }, { shortName: 'dom-edge-webpack', entryPoints: [ @@ -160,7 +182,6 @@ module.exports = [ { shortName: 'dom-node-esm', entryPoints: [ - 'react-server-dom-esm/client.browser', 'react-server-dom-esm/server.node', 'react-server-dom-esm/client.node', ], @@ -175,7 +196,6 @@ module.exports = [ 'react-dom/src/server/ReactDOMFizzServerNode.js', // react-dom/server.node 'react-dom/src/server/ReactDOMFizzStaticNode.js', 'react-server-dom-esm', - 'react-server-dom-esm/client.browser', 'react-server-dom-esm/client.node', 'react-server-dom-esm/server', 'react-server-dom-esm/server.node',