facebook/reactMITd083ec1Report / request removal

Rollup Bundles & Module Forks

The Rollup build turns declarative bundle entries into artifacts for multiple environments, release modes, module formats, and package layouts. A bundle definition supplies an entry module, supported bundle types, a global name, externals, and packaging-related options.

This indirection exists because the same React entry point must be built differently for Node, ESM, Facebook WWW, and React Native consumers. The build also replaces shared modules with environment-specific forks before Rollup resolves the final dependency graph.

Sources: scripts/rollup/bundles.js:69-1304, scripts/rollup/bundles.js:10-31, scripts/rollup/build.js:832-915, scripts/rollup/build.js:589-637, scripts/rollup/modules.js:63-80

Core concepts

Bundle definition

A bundle definition is one object in bundles describing an entry point and the bundle types that may be produced from it.

For example, the react entry supports Node, Facebook WWW, and React Native Facebook variants, declares React as its global name, and keeps ReactNativeInternalFeatureFlags external.

Sources: scripts/rollup/bundles.js:69-1304

Bundle type

A bundle type is a named target such as NODE_DEV, NODE_PROD, FB_WWW_DEV, or RN_FB_PROD that controls filename, format, production status, wrappers, and output location.

Sources: scripts/rollup/bundles.js:10-31, scripts/rollup/build.js:226-252, scripts/rollup/build.js:254-282

Module fork

A module fork is a replacement mapping from a shared source module to a target file selected from the current entry, bundle type, module type, and dependency set.

Sources: scripts/rollup/modules.js:63-80, scripts/rollup/forks.js:52-475

Wrapper

A wrapper is generated source around the Rollup result, adding environment guards, reconciler exports, module-boundary registration, or a license header.

Sources: scripts/rollup/wrappers.js:507-570, scripts/rollup/wrappers.js:572-592

How one bundle definition expands

buildEverything enumerates every configured bundle and pairs it with the complete set of known bundle types, then shouldSkipBundle removes combinations not declared by that bundle or not requested by command-line filters.

The react definition demonstrates the expansion rule: one entry is eligible for eight targets, while other entries select narrower sets.

Definition attributeMeaningExample
bundleTypesAllowed output variantsNODE_DEV, FB_WWW_PROD, RN_FB_PROD
entrySource entry resolved by Rollupreact
globalGlobal name used by environment wrappers and filenamesReact
externalsPackages or globals kept outside the bundleReactNativeInternalFeatureFlags
moduleTypeBroad package roleISOMORPHIC
wrapWithModuleBoundariesWhether eligible bundles register internal module boundariestrue

These fields are read while createBundle constructs the Rollup configuration for each selected pair.

The output filename is decided before Rollup runs: getFilename derives a base name from bundle.name or bundle.entry, normalizes path separators, and adds a suffix based on the bundle type. Node and ESM development or production builds use .development.js and .production.js; Facebook WWW and React Native builds use the configured global name with -dev.js, -prod.js, or -profiling.js.

case NODE_DEV:
  return `${name}.development.js`;
case NODE_PROD:
  return `${name}.production.js`;
case FB_WWW_DEV:
case RN_OSS_DEV:
case RN_FB_DEV:
  return `${globalName}-dev.js`;

The important distinction is that filename selection and directory selection are separate: getFilename chooses the artifact name, while getBundleOutputPath chooses where that name is written.

This workflow shows how one table entry becomes a concrete build job and then reaches forks, output paths, and packaging.

Bundle expansion workflow — How does one bundle definition become an environment-specific artifact?

Evidence

Sources: scripts/rollup/build.js:832-915, scripts/rollup/build.js:552-587, scripts/rollup/bundles.js:69-1304, scripts/rollup/build.js:639-755, scripts/rollup/bundles.js:1326-1366, scripts/rollup/packaging.js:48-115

How module forks replace shared files

createBundle first resolves an entry fork for Facebook or Facebook React Native targets, preferring modern or classic Facebook files, then development-specific files, then generic Facebook, stable or experimental, and finally the plain entry.

The module-level fork path is separate from entry-fork selection. getPlugins calls Modules.getForks, and the returned mapping is installed through useForks.

getForks iterates the fork registry, computes package dependencies with getDependencies, invokes each fork selector, and records every non-null target module.

For shared internals, the forks registry can choose client or server implementations based on the entry and the react-server condition, and it selects Facebook WWW-specific React DOM internals for the Facebook WWW bundle types.

The same registry contains environment-specific feature-flag rules. Thus a shared import such as ReactFeatureFlags.js can be replaced by a renderer- or channel-specific file such as ReactFeatureFlags.www.js or a React Native variant before bundling.

A fork selector also checks dependency declarations when an entry outside the owning package tries to use React or React DOM internals; it returns an error-producing target rather than silently permitting an undeclared dependency.

This sequence captures the build-time call order for one selected bundle/type pair.

Bundle construction order — When are entry forks, module forks, and output paths resolved?

Evidence

Sources: scripts/rollup/build.js:589-637, scripts/rollup/build.js:639-755, scripts/rollup/build.js:355-550, scripts/rollup/modules.js:63-80, scripts/rollup/modules.js:49-60, scripts/rollup/forks.js:52-475

What wrappers, externals, and packaging add

The plugin pipeline removes individual module banners, applies Babel and Flow transformations, replaces __DEV__, __PROFILE__, and process.env.NODE_ENV, installs forks, and renders top-level definitions and license headers around the final chunk.

Top-level wrappers leave ESM and production bundles unchanged, but development bundles add environment guards. NODE_DEV checks process.env.NODE_ENV, while Facebook WWW and both React Native development families check __DEV__.

If wrapWithModuleBoundaries is enabled, eligible development and profiling bundles remove the source use strict header and call wrapWithRegisterInternalModule, which registers module start and stop events with the DevTools global hook.

Reconciler bundles use a different wrapper family: they export a $$$reconciler function, create an exports object, attach a default export, and mark the result as an ES module; development variants additionally guard that export.

License wrapping is applied in a later renderChunk hook. Browser-script bundles bypass extra wrapper comments, while other supported bundle types select a license-header function keyed by bundle type.

The external function keeps declared dependencies and known peer globals outside the Rollup graph. It rejects imports into a dependency’s /src/ path because published dependencies do not provide those source files.

Finally, buildEverything copies WWW and React Native shims, prepares npm packages, filters entry points that have no bundle in the current release channel, and can synchronize Facebook or WWW outputs.

Sources: scripts/rollup/build.js:355-550, scripts/rollup/wrappers.js:507-570, scripts/rollup/wrappers.js:572-592, scripts/rollup/wrappers.js:58-169, scripts/rollup/wrappers.js:32-51, scripts/rollup/wrappers.js:171-263, scripts/rollup/build.js:515-523, scripts/rollup/build.js:639-755, scripts/rollup/build.js:672-690, scripts/rollup/build.js:832-915, scripts/rollup/packaging.js:135-137, scripts/rollup/packaging.js:171-251

How it connects

The rollup scripts consume package dependency metadata and shared module policy through getDependencies, getPeerGlobals, and getImportSideEffects; this keeps externalization and tree-shaking decisions aligned with package declarations.

For repository orientation, start with Repository Map and React Overview. Release-channel behavior and feature-flag selection are described in Build, Test, and Feature Flags, while the surrounding script responsibilities are covered by Release, Build & Lint Scripts.

Packaging hands the generated files into the npm preparation flow, including npm pack extraction with paths remapped from the archive’s package/ prefix.

Sources: scripts/rollup/modules.js:49-60, scripts/rollup/modules.js:40-46, scripts/rollup/modules.js:82-84, scripts/rollup/build.js:832-915, scripts/rollup/forks.js:52-475, scripts/rollup/packaging.js:139-156

Key takeaways

  • bundles defines eligible bundle/type combinations; buildEverything expands and filters them.
  • getFilename chooses the filename; getBundleOutputPath chooses the directory.
  • getForks maps shared modules to entry-, renderer-, and channel-specific files before Rollup runs.
  • Wrappers add development guards, reconciler exports, DevTools boundaries, and license headers.
  • createBundle handles externals, while packaging copies shims, filters entry points, and prepares npm artifacts.

Want this for your repos?

Try Angada AI Wiki