diff --git a/.flowconfig b/.flowconfig
index e67a199a5773f..a577fa72c607d 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -8,9 +8,6 @@
 <PROJECT_ROOT>/scripts/rollup/shims/facebook-www/.*
 <PROJECT_ROOT>/scripts/rollup/shims/react-native/.*
 
-# Note: intentionally *don't* ignore /scripts/rollup/shims/rollup/
-# because it is part of the build and isn't external.
-
 <PROJECT_ROOT>/.*/node_modules/y18n/.*
 <PROJECT_ROOT>/node_modules/chrome-devtools-frontend/.*
 <PROJECT_ROOT>/node_modules/devtools-timeline-model/.*
diff --git a/package.json b/package.json
index df774812b03ab..132bd2801d27a 100644
--- a/package.json
+++ b/package.json
@@ -80,7 +80,6 @@
     "prop-types": "^15.6.0",
     "rimraf": "^2.6.1",
     "rollup": "^0.51.7",
-    "rollup-plugin-alias": "^1.2.1",
     "rollup-plugin-babel": "^2.7.1",
     "rollup-plugin-closure-compiler-js": "^1.0.4",
     "rollup-plugin-commonjs": "^8.2.6",
diff --git a/packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.internal.js b/packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.internal.js
index 998a54107e7eb..825de238f5f80 100644
--- a/packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.internal.js
+++ b/packages/react-cs-renderer/src/__tests__/ReactNativeCS-test.internal.js
@@ -13,7 +13,7 @@ var React;
 var ReactNativeCS;
 
 jest.mock('shared/ReactFeatureFlags', () =>
-  require('../ReactNativeCSFeatureFlags'),
+  require('shared/forks/ReactFeatureFlags.native-cs'),
 );
 
 describe('ReactNativeCS', () => {
diff --git a/scripts/rollup/shims/rollup/ReactCurrentOwner-www.js b/packages/react/src/forks/ReactCurrentOwner.www.js
similarity index 100%
rename from scripts/rollup/shims/rollup/ReactCurrentOwner-www.js
rename to packages/react/src/forks/ReactCurrentOwner.www.js
diff --git a/packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js b/packages/shared/forks/ReactFeatureFlags.native-cs.js
similarity index 93%
rename from packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js
rename to packages/shared/forks/ReactFeatureFlags.native-cs.js
index b3428288290af..2b1d771729a3b 100644
--- a/packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js
+++ b/packages/shared/forks/ReactFeatureFlags.native-cs.js
@@ -10,7 +10,7 @@
 import invariant from 'fbjs/lib/invariant';
 
 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
-import typeof * as CSFeatureFlagsType from './ReactNativeCSFeatureFlags';
+import typeof * as CSFeatureFlagsType from './ReactFeatureFlags.native-cs';
 
 export const debugRenderPhaseSideEffects = false;
 export const enableAsyncSubtreeAPI = true;
diff --git a/packages/react-native-renderer/src/ReactNativeFeatureFlags.js b/packages/shared/forks/ReactFeatureFlags.native.js
similarity index 94%
rename from packages/react-native-renderer/src/ReactNativeFeatureFlags.js
rename to packages/shared/forks/ReactFeatureFlags.native.js
index f5e6e3a187bac..90447cfcecafd 100644
--- a/packages/react-native-renderer/src/ReactNativeFeatureFlags.js
+++ b/packages/shared/forks/ReactFeatureFlags.native.js
@@ -10,7 +10,7 @@
 import invariant from 'fbjs/lib/invariant';
 
 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
-import typeof * as FeatureFlagsShimType from './ReactNativeFeatureFlags';
+import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.native';
 
 // Re-export dynamic flags from the fbsource version.
 export const {debugRenderPhaseSideEffects} = require('ReactFeatureFlags');
diff --git a/scripts/rollup/shims/rollup/ReactFeatureFlags-www.js b/packages/shared/forks/ReactFeatureFlags.www.js
similarity index 99%
rename from scripts/rollup/shims/rollup/ReactFeatureFlags-www.js
rename to packages/shared/forks/ReactFeatureFlags.www.js
index 607ad30b724af..5858fad2a574f 100644
--- a/scripts/rollup/shims/rollup/ReactFeatureFlags-www.js
+++ b/packages/shared/forks/ReactFeatureFlags.www.js
@@ -8,7 +8,7 @@
  */
 
 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
-import typeof * as FeatureFlagsShimType from './ReactFeatureFlags-www';
+import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.www';
 
 // Re-export dynamic flags from the www version.
 export const {
diff --git a/scripts/rollup/shims/rollup/lowPriorityWarning-www.js b/packages/shared/forks/lowPriorityWarning.www.js
similarity index 100%
rename from scripts/rollup/shims/rollup/lowPriorityWarning-www.js
rename to packages/shared/forks/lowPriorityWarning.www.js
diff --git a/scripts/rollup/shims/rollup/assign-umd.js b/packages/shared/forks/object-assign.umd.js
similarity index 100%
rename from scripts/rollup/shims/rollup/assign-umd.js
rename to packages/shared/forks/object-assign.umd.js
diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js
index eda06fb8fd6ab..f57d425034e07 100644
--- a/scripts/rollup/build.js
+++ b/scripts/rollup/build.js
@@ -4,20 +4,19 @@ const rollup = require('rollup').rollup;
 const babel = require('rollup-plugin-babel');
 const closure = require('rollup-plugin-closure-compiler-js');
 const commonjs = require('rollup-plugin-commonjs');
-const alias = require('rollup-plugin-alias');
 const prettier = require('rollup-plugin-prettier');
 const replace = require('rollup-plugin-replace');
 const stripBanner = require('rollup-plugin-strip-banner');
 const chalk = require('chalk');
-const join = require('path').join;
-const resolve = require('path').resolve;
-const resolvePlugin = require('rollup-plugin-node-resolve');
+const path = require('path');
+const resolve = require('rollup-plugin-node-resolve');
 const fs = require('fs');
 const rimraf = require('rimraf');
 const argv = require('minimist')(process.argv.slice(2));
 const Modules = require('./modules');
 const Bundles = require('./bundles');
 const sizes = require('./plugins/sizes-plugin');
+const useForks = require('./plugins/use-forks-plugin');
 const Stats = require('./stats');
 const extractErrorCodes = require('../error-codes/extract-errors');
 const syncReactDom = require('./sync').syncReactDom;
@@ -88,7 +87,7 @@ function getBabelConfig(updateBabelOptions, bundleType, filename) {
       return Object.assign({}, options, {
         plugins: options.plugins.concat([
           // Use object-assign polyfill in open source
-          resolve('./scripts/babel/transform-object-assign-require'),
+          path.resolve('./scripts/babel/transform-object-assign-require'),
           // Minify invariant messages
           require('../error-codes/replace-invariant-error-codes'),
           // Wrap warning() calls in a __DEV__ check so they are stripped from production.
@@ -113,11 +112,11 @@ function handleRollupWarnings(warning) {
       );
     }
     const importSideEffects = Modules.getImportSideEffects();
-    const path = match[1];
-    if (typeof importSideEffects[path] !== 'boolean') {
+    const externalModule = match[1];
+    if (typeof importSideEffects[externalModule] !== 'boolean') {
       throw new Error(
         'An external module "' +
-          path +
+          externalModule +
           '" is used in a DEV-only code path ' +
           'but we do not know if it is safe to omit an unused require() to it in production. ' +
           'Please add it to the `importSideEffects` list in `scripts/rollup/modules.js`.'
@@ -218,11 +217,10 @@ function getPlugins(
   bundleType,
   globalName,
   moduleType,
-  modulesToStub,
-  featureFlags
+  modulesToStub
 ) {
   const findAndRecordErrorCodes = extractErrorCodes(errorCodeOpts);
-  const shims = Modules.getShims(bundleType, entry, featureFlags);
+  const forks = Modules.getForks(bundleType, entry);
   const isProduction = isProductionBundleType(bundleType);
   const isInGlobalScope = bundleType === UMD_DEV || bundleType === UMD_PROD;
   const isFBBundle = bundleType === FB_DEV || bundleType === FB_PROD;
@@ -236,10 +234,10 @@ function getPlugins(
         return source;
       },
     },
-    // Shim some modules for www custom behavior and optimizations.
-    alias(shims),
+    // Shim any modules that need forking in this environment.
+    useForks(forks),
     // Use Node resolution mechanism.
-    resolvePlugin({
+    resolve({
       skip: externals,
     }),
     // Remove license headers from individual modules
@@ -384,8 +382,7 @@ async function createBundle(bundle, bundleType) {
         bundleType,
         bundle.global,
         bundle.moduleType,
-        bundle.modulesToStub,
-        bundle.featureFlags
+        bundle.modulesToStub
       ),
       // We can't use getters in www.
       legacy: bundleType === FB_DEV || bundleType === FB_PROD,
@@ -439,9 +436,9 @@ rimraf('build', async () => {
     // create a new build directory
     fs.mkdirSync('build');
     // create the packages folder for NODE+UMD bundles
-    fs.mkdirSync(join('build', 'packages'));
+    fs.mkdirSync(path.join('build', 'packages'));
     // create the dist folder for UMD bundles
-    fs.mkdirSync(join('build', 'dist'));
+    fs.mkdirSync(path.join('build', 'dist'));
 
     await Packaging.createFacebookWWWBuild();
     await Packaging.createReactNativeBuild();
@@ -462,11 +459,11 @@ rimraf('build', async () => {
     }
 
     if (syncFbsource) {
-      await syncReactNative(join('build', 'react-native'), syncFbsource);
-      await syncReactNativeRT(join('build', 'react-rt'), syncFbsource);
-      await syncReactNativeCS(join('build', 'react-cs'), syncFbsource);
+      await syncReactNative(path.join('build', 'react-native'), syncFbsource);
+      await syncReactNativeRT(path.join('build', 'react-rt'), syncFbsource);
+      await syncReactNativeCS(path.join('build', 'react-cs'), syncFbsource);
     } else if (syncWww) {
-      await syncReactDom(join('build', 'facebook-www'), syncWww);
+      await syncReactDom(path.join('build', 'facebook-www'), syncWww);
     }
 
     console.log(Stats.printResults());
diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js
index abab79daafc15..335ebf6aec1c7 100644
--- a/scripts/rollup/bundles.js
+++ b/scripts/rollup/bundles.js
@@ -129,7 +129,6 @@ const bundles = [
       'deepFreezeAndThrowOnMutationInDev',
       'flattenStyle',
     ],
-    featureFlags: 'react-native-renderer/src/ReactNativeFeatureFlags',
   },
 
   /******* React Native RT *******/
@@ -156,7 +155,6 @@ const bundles = [
     entry: 'react-cs-renderer',
     global: 'ReactCSRenderer',
     externals: ['CSStatefulComponent'],
-    featureFlags: 'react-cs-renderer/src/ReactNativeCSFeatureFlags',
   },
 
   /******* React Test Renderer *******/
diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js
new file mode 100644
index 0000000000000..0c3845353d008
--- /dev/null
+++ b/scripts/rollup/forks.js
@@ -0,0 +1,71 @@
+'use strict';
+
+const bundleTypes = require('./bundles').bundleTypes;
+
+const UMD_DEV = bundleTypes.UMD_DEV;
+const UMD_PROD = bundleTypes.UMD_PROD;
+const FB_DEV = bundleTypes.FB_DEV;
+const FB_PROD = bundleTypes.FB_PROD;
+
+// 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.
+const forks = Object.freeze({
+  // Optimization: for UMDs, use object-assign polyfill that is already a part
+  // of the React package instead of bundling it again.
+  'object-assign': (bundleType, entry, dependencies) => {
+    if (bundleType !== UMD_DEV && bundleType !== UMD_PROD) {
+      // It's only relevant for UMD bundles since that's where the duplication
+      // happens. Other bundles just require('object-assign') anyway.
+      return null;
+    }
+    if (dependencies.indexOf('react') === -1) {
+      // We can only apply the optimizations to bundle that depend on React
+      // because we read assign() from an object exposed on React internals.
+      return null;
+    }
+    // We can use the fork!
+    return 'shared/forks/object-assign.umd.js';
+  },
+
+  // We have a few forks for different environments.
+  'shared/ReactFeatureFlags': (bundleType, entry) => {
+    switch (entry) {
+      case 'react-native-renderer':
+        return 'shared/forks/ReactFeatureFlags.native.js';
+      case 'react-cs-renderer':
+        return 'shared/forks/ReactFeatureFlags.native-cs.js';
+      default:
+        switch (bundleType) {
+          case FB_DEV:
+          case FB_PROD:
+            return 'shared/forks/ReactFeatureFlags.www.js';
+        }
+    }
+    return null;
+  },
+
+  // This logic is forked on www to blacklist warnings.
+  'shared/lowPriorityWarning': (bundleType, entry) => {
+    switch (bundleType) {
+      case FB_DEV:
+      case FB_PROD:
+        return 'shared/forks/lowPriorityWarning.www.js';
+      default:
+        return null;
+    }
+  },
+
+  // In FB bundles, we preserve an inline require to ReactCurrentOwner.
+  // See the explanation in FB version of ReactCurrentOwner in www:
+  'react/src/ReactCurrentOwner': (bundleType, entry) => {
+    switch (bundleType) {
+      case FB_DEV:
+      case FB_PROD:
+        return 'react/src/forks/ReactCurrentOwner.www.js';
+      default:
+        return null;
+    }
+  },
+});
+
+module.exports = forks;
diff --git a/scripts/rollup/modules.js b/scripts/rollup/modules.js
index a60f8c94dddad..e49cb2c4d6646 100644
--- a/scripts/rollup/modules.js
+++ b/scripts/rollup/modules.js
@@ -1,31 +1,11 @@
 'use strict';
 
 const path = require('path');
+const forks = require('./forks');
 const bundleTypes = require('./bundles').bundleTypes;
 
 const UMD_DEV = bundleTypes.UMD_DEV;
 const UMD_PROD = bundleTypes.UMD_PROD;
-const FB_DEV = bundleTypes.FB_DEV;
-const FB_PROD = bundleTypes.FB_PROD;
-
-// Bundles exporting globals that other modules rely on.
-const knownGlobals = Object.freeze({
-  react: 'React',
-  'react-dom': 'ReactDOM',
-});
-
-// Redirect some modules to Haste forks in www.
-// Assuming their names in www are the same, and also match
-// imported names in corresponding ./shims/rollup/*-www.js shims.
-const forkedFBModules = Object.freeze([
-  // At FB, we don't know them statically:
-  'shared/ReactFeatureFlags',
-  // This logic is also forked internally.
-  'shared/lowPriorityWarning',
-  // In FB bundles, we preserve an inline require to ReactCurrentOwner.
-  // See the explanation in FB version of ReactCurrentOwner in www:
-  'react/src/ReactCurrentOwner',
-]);
 
 // For any external that is used in a DEV-only condition, explicitly
 // specify whether it has side effects during import or not. This lets
@@ -41,6 +21,12 @@ const importSideEffects = Object.freeze({
   deepFreezeAndThrowOnMutationInDev: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
 });
 
+// Bundles exporting globals that other modules rely on.
+const knownGlobals = Object.freeze({
+  react: 'React',
+  'react-dom': 'ReactDOM',
+});
+
 // Given ['react'] in bundle externals, returns { 'react': 'React' }.
 function getPeerGlobals(externals, moduleType) {
   const peerGlobals = {};
@@ -62,72 +48,35 @@ function getDependencies(bundleType, entry) {
     path.dirname(require.resolve(entry))
   ) + '/package.json');
   // Both deps and peerDeps are assumed as accessible.
-  let deps = Array.from(
+  return Array.from(
     new Set([
       ...Object.keys(packageJson.dependencies || {}),
       ...Object.keys(packageJson.peerDependencies || {}),
     ])
   );
-  // In www, forked modules are also require-able.
-  if (bundleType === FB_DEV || bundleType === FB_PROD) {
-    deps = [...deps, ...forkedFBModules.map(name => path.basename(name))];
-  }
-  return deps;
 }
 
-function getImportSideEffects() {
-  return importSideEffects;
+// Hijacks some modules for optimization and integration reasons.
+function getForks(bundleType, entry) {
+  const forksForBundle = {};
+  Object.keys(forks).forEach(srcModule => {
+    const dependencies = getDependencies(bundleType, entry);
+    const targetModule = forks[srcModule](bundleType, entry, dependencies);
+    if (targetModule === null) {
+      return;
+    }
+    forksForBundle[srcModule] = targetModule;
+  });
+  return forksForBundle;
 }
 
-// Hijacks some modules for optimization and integration reasons.
-function getShims(bundleType, entry, featureFlags) {
-  const shims = {};
-  switch (bundleType) {
-    case UMD_DEV:
-    case UMD_PROD:
-      if (getDependencies(bundleType, entry).indexOf('react') !== -1) {
-        // Optimization: rely on object-assign polyfill that is already a part
-        // of the React package instead of bundling it again.
-        shims['object-assign'] = path.resolve(
-          __dirname + '/shims/rollup/assign-umd.js'
-        );
-      }
-      break;
-    case FB_DEV:
-    case FB_PROD:
-      // FB forks a few modules in www that are usually bundled.
-      // Instead of bundling them, they need to be kept as require()s in the
-      // final bundles so that they import www modules with the same names.
-      // Rollup doesn't make it very easy to rewrite and ignore such a require,
-      // so we resort to using a shim that re-exports the www module, and then
-      // treating shim's target destinations as external (see getDependencies).
-      forkedFBModules.forEach(srcPath => {
-        const fileName = path.parse(srcPath).name;
-        const shimPath = path.resolve(
-          __dirname + `/shims/rollup/${fileName}-www.js`
-        );
-        shims[srcPath] = shimPath;
-        // <hack>
-        // Unfortunately the above doesn't work for relative imports,
-        // and Rollup isn't smart enough to understand they refer to the same file.
-        // We should come up with a real fix for this, but for now this will do.
-        // FIXME: this is gross.
-        shims['./' + fileName] = shimPath;
-        shims['../' + fileName] = shimPath;
-        // We don't have deeper relative requires between source files.
-        // </hack>
-      });
-      break;
-  }
-  if (featureFlags) {
-    shims['shared/ReactFeatureFlags'] = require.resolve(featureFlags);
-  }
-  return shims;
+function getImportSideEffects() {
+  return importSideEffects;
 }
 
 module.exports = {
   getImportSideEffects,
   getPeerGlobals,
   getDependencies,
-  getShims,
+  getForks,
 };
diff --git a/scripts/rollup/plugins/sizes-plugin.js b/scripts/rollup/plugins/sizes-plugin.js
index c891d436b27e0..514795464d52b 100644
--- a/scripts/rollup/plugins/sizes-plugin.js
+++ b/scripts/rollup/plugins/sizes-plugin.js
@@ -1,3 +1,11 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
 const gzip = require('gzip-size');
 
 module.exports = function sizes(options) {
diff --git a/scripts/rollup/plugins/use-forks-plugin.js b/scripts/rollup/plugins/use-forks-plugin.js
new file mode 100644
index 0000000000000..3ba56dd3a059e
--- /dev/null
+++ b/scripts/rollup/plugins/use-forks-plugin.js
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+'use strict';
+
+const path = require('path');
+
+let resolveCache = new Map();
+function useForks(forks) {
+  let resolvedForks = {};
+  Object.keys(forks).forEach(srcModule => {
+    const targetModule = forks[srcModule];
+    resolvedForks[require.resolve(srcModule)] = require.resolve(targetModule);
+  });
+  return {
+    resolveId(importee, importer) {
+      if (!importer || !importee) {
+        return null;
+      }
+      let resolvedImportee = null;
+      let cacheKey = `${importer}:::${importee}`;
+      if (resolveCache.has(cacheKey)) {
+        // Avoid hitting file system if possible.
+        resolvedImportee = resolveCache.get(cacheKey);
+      } else {
+        try {
+          resolvedImportee = require.resolve(importee, {
+            paths: [path.dirname(importer)],
+          });
+        } catch (err) {
+          // Not our fault, let Rollup fail later.
+        }
+        if (resolvedImportee) {
+          resolveCache.set(cacheKey, resolvedImportee);
+        }
+      }
+      if (resolvedImportee && resolvedForks.hasOwnProperty(resolvedImportee)) {
+        // We found a fork!
+        return resolvedForks[resolvedImportee];
+      }
+      return null;
+    },
+  };
+}
+
+module.exports = useForks;
diff --git a/yarn.lock b/yarn.lock
index 4618809712be8..3a9bfa57d67c4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4122,12 +4122,6 @@ rimraf@^2.5.4:
   dependencies:
     glob "^7.0.5"
 
-rollup-plugin-alias@^1.2.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/rollup-plugin-alias/-/rollup-plugin-alias-1.3.1.tgz#a9152fec4b6a6510dae93989517ca7853c32a6fa"
-  dependencies:
-    slash "^1.0.0"
-
 rollup-plugin-babel@^2.7.1:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-2.7.1.tgz#16528197b0f938a1536f44683c7a93d573182f57"