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 attribute | Meaning | Example |
|---|---|---|
bundleTypes | Allowed output variants | NODE_DEV, FB_WWW_PROD, RN_FB_PROD |
entry | Source entry resolved by Rollup | react |
global | Global name used by environment wrappers and filenames | React |
externals | Packages or globals kept outside the bundle | ReactNativeInternalFeatureFlags |
moduleType | Broad package role | ISOMORPHIC |
wrapWithModuleBoundaries | Whether eligible bundles register internal module boundaries | true |
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.
Evidence
- bundle-tablescripts/rollup/bundles.js:69
- build-loopscripts/rollup/build.js:832
- bundle-jobscripts/rollup/build.js:639
- fork-resolutionscripts/rollup/modules.js:63
- output-pathscripts/rollup/packaging.js:48
- package-prepscripts/rollup/packaging.js:275
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.
Evidence
- bundle-jobscripts/rollup/build.js:639
- entry-forkscripts/rollup/build.js:589
- plugin-stagescripts/rollup/build.js:355
- module-forksscripts/rollup/modules.js:63
- output-pathscripts/rollup/packaging.js:48
- rollup-optionsscripts/rollup/build.js:204
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
bundlesdefines eligible bundle/type combinations;buildEverythingexpands and filters them.getFilenamechooses the filename;getBundleOutputPathchooses the directory.getForksmaps shared modules to entry-, renderer-, and channel-specific files before Rollup runs.- Wrappers add development guards, reconciler exports, DevTools boundaries, and license headers.
createBundlehandles externals, while packaging copies shims, filters entry points, and prepares npm artifacts.