1. 'use strict';
    
  2. 
    
  3. const fs = require('node:fs');
    
  4. const {bundleTypes, moduleTypes} = require('./bundles');
    
  5. const inlinedHostConfigs = require('../shared/inlinedHostConfigs');
    
  6. 
    
  7. const {
    
  8.   UMD_DEV,
    
  9.   UMD_PROD,
    
  10.   UMD_PROFILING,
    
  11.   FB_WWW_DEV,
    
  12.   FB_WWW_PROD,
    
  13.   FB_WWW_PROFILING,
    
  14.   RN_OSS_DEV,
    
  15.   RN_OSS_PROD,
    
  16.   RN_OSS_PROFILING,
    
  17.   RN_FB_DEV,
    
  18.   RN_FB_PROD,
    
  19.   RN_FB_PROFILING,
    
  20. } = bundleTypes;
    
  21. const {RENDERER, RECONCILER} = moduleTypes;
    
  22. 
    
  23. const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL;
    
  24. 
    
  25. // Default to building in experimental mode. If the release channel is set via
    
  26. // an environment variable, then check if it's "experimental".
    
  27. const __EXPERIMENTAL__ =
    
  28.   typeof RELEASE_CHANNEL === 'string'
    
  29.     ? RELEASE_CHANNEL === 'experimental'
    
  30.     : true;
    
  31. 
    
  32. function findNearestExistingForkFile(path, segmentedIdentifier, suffix) {
    
  33.   const segments = segmentedIdentifier.split('-');
    
  34.   while (segments.length) {
    
  35.     const candidate = segments.join('-');
    
  36.     const forkPath = path + candidate + suffix;
    
  37.     try {
    
  38.       fs.statSync(forkPath);
    
  39.       return forkPath;
    
  40.     } catch (error) {
    
  41.       // Try the next candidate.
    
  42.     }
    
  43.     segments.pop();
    
  44.   }
    
  45.   return null;
    
  46. }
    
  47. 
    
  48. // If you need to replace a file with another file for a specific environment,
    
  49. // add it to this list with the logic for choosing the right replacement.
    
  50. 
    
  51. // Fork paths are relative to the project root. They must include the full path,
    
  52. // including the extension. We intentionally don't use Node's module resolution
    
  53. // algorithm because 1) require.resolve doesn't work with ESM modules, and 2)
    
  54. // the behavior is easier to predict.
    
  55. const forks = Object.freeze({
    
  56.   // Without this fork, importing `shared/ReactSharedInternals` inside
    
  57.   // the `react` package itself would not work due to a cyclical dependency.
    
  58.   './packages/shared/ReactSharedInternals.js': (
    
  59.     bundleType,
    
  60.     entry,
    
  61.     dependencies
    
  62.   ) => {
    
  63.     if (entry === 'react') {
    
  64.       return './packages/react/src/ReactSharedInternalsClient.js';
    
  65.     }
    
  66.     if (entry === 'react/src/ReactSharedSubset.js') {
    
  67.       return './packages/react/src/ReactSharedInternalsServer.js';
    
  68.     }
    
  69.     if (!entry.startsWith('react/') && dependencies.indexOf('react') === -1) {
    
  70.       // React internals are unavailable if we can't reference the package.
    
  71.       // We return an error because we only want to throw if this module gets used.
    
  72.       return new Error(
    
  73.         'Cannot use a module that depends on ReactSharedInternals ' +
    
  74.           'from "' +
    
  75.           entry +
    
  76.           '" because it does not declare "react" in the package ' +
    
  77.           'dependencies or peerDependencies.'
    
  78.       );
    
  79.     }
    
  80.     return null;
    
  81.   },
    
  82. 
    
  83.   // Without this fork, importing `shared/ReactDOMSharedInternals` inside
    
  84.   // the `react-dom` package itself would not work due to a cyclical dependency.
    
  85.   './packages/shared/ReactDOMSharedInternals.js': (
    
  86.     bundleType,
    
  87.     entry,
    
  88.     dependencies
    
  89.   ) => {
    
  90.     if (
    
  91.       entry === 'react-dom' ||
    
  92.       entry === 'react-dom/server-rendering-stub' ||
    
  93.       entry === 'react-dom/src/ReactDOMSharedSubset.js'
    
  94.     ) {
    
  95.       return './packages/react-dom/src/ReactDOMSharedInternals.js';
    
  96.     }
    
  97.     if (
    
  98.       !entry.startsWith('react-dom/') &&
    
  99.       dependencies.indexOf('react-dom') === -1
    
  100.     ) {
    
  101.       // React DOM internals are unavailable if we can't reference the package.
    
  102.       // We return an error because we only want to throw if this module gets used.
    
  103.       return new Error(
    
  104.         'Cannot use a module that depends on ReactDOMSharedInternals ' +
    
  105.           'from "' +
    
  106.           entry +
    
  107.           '" because it does not declare "react-dom" in the package ' +
    
  108.           'dependencies or peerDependencies.'
    
  109.       );
    
  110.     }
    
  111.     return null;
    
  112.   },
    
  113. 
    
  114.   // We have a few forks for different environments.
    
  115.   './packages/shared/ReactFeatureFlags.js': (bundleType, entry) => {
    
  116.     switch (entry) {
    
  117.       case 'react-native-renderer':
    
  118.         switch (bundleType) {
    
  119.           case RN_FB_DEV:
    
  120.           case RN_FB_PROD:
    
  121.           case RN_FB_PROFILING:
    
  122.             return './packages/shared/forks/ReactFeatureFlags.native-fb.js';
    
  123.           case RN_OSS_DEV:
    
  124.           case RN_OSS_PROD:
    
  125.           case RN_OSS_PROFILING:
    
  126.             return './packages/shared/forks/ReactFeatureFlags.native-oss.js';
    
  127.           default:
    
  128.             throw Error(
    
  129.               `Unexpected entry (${entry}) and bundleType (${bundleType})`
    
  130.             );
    
  131.         }
    
  132.       case 'react-native-renderer/fabric':
    
  133.         switch (bundleType) {
    
  134.           case RN_FB_DEV:
    
  135.           case RN_FB_PROD:
    
  136.           case RN_FB_PROFILING:
    
  137.             return './packages/shared/forks/ReactFeatureFlags.native-fb.js';
    
  138.           case RN_OSS_DEV:
    
  139.           case RN_OSS_PROD:
    
  140.           case RN_OSS_PROFILING:
    
  141.             return './packages/shared/forks/ReactFeatureFlags.native-oss.js';
    
  142.           default:
    
  143.             throw Error(
    
  144.               `Unexpected entry (${entry}) and bundleType (${bundleType})`
    
  145.             );
    
  146.         }
    
  147.       case 'react-test-renderer':
    
  148.         switch (bundleType) {
    
  149.           case RN_FB_DEV:
    
  150.           case RN_FB_PROD:
    
  151.           case RN_FB_PROFILING:
    
  152.           case RN_OSS_DEV:
    
  153.           case RN_OSS_PROD:
    
  154.           case RN_OSS_PROFILING:
    
  155.             return './packages/shared/forks/ReactFeatureFlags.test-renderer.native.js';
    
  156.           case FB_WWW_DEV:
    
  157.           case FB_WWW_PROD:
    
  158.           case FB_WWW_PROFILING:
    
  159.             return './packages/shared/forks/ReactFeatureFlags.test-renderer.www.js';
    
  160.         }
    
  161.         return './packages/shared/forks/ReactFeatureFlags.test-renderer.js';
    
  162.       default:
    
  163.         switch (bundleType) {
    
  164.           case FB_WWW_DEV:
    
  165.           case FB_WWW_PROD:
    
  166.           case FB_WWW_PROFILING:
    
  167.             return './packages/shared/forks/ReactFeatureFlags.www.js';
    
  168.           case RN_FB_DEV:
    
  169.           case RN_FB_PROD:
    
  170.           case RN_FB_PROFILING:
    
  171.             return './packages/shared/forks/ReactFeatureFlags.native-fb.js';
    
  172.         }
    
  173.     }
    
  174.     return null;
    
  175.   },
    
  176. 
    
  177.   './packages/scheduler/index.js': (bundleType, entry, dependencies) => {
    
  178.     switch (bundleType) {
    
  179.       case UMD_DEV:
    
  180.       case UMD_PROD:
    
  181.       case UMD_PROFILING:
    
  182.         if (dependencies.indexOf('react') === -1) {
    
  183.           // It's only safe to use this fork for modules that depend on React,
    
  184.           // because they read the re-exported API from the SECRET_INTERNALS object.
    
  185.           return null;
    
  186.         }
    
  187.         // Optimization: for UMDs, use the API that is already a part of the React
    
  188.         // package instead of requiring it to be loaded via a separate <script> tag
    
  189.         return './packages/shared/forks/Scheduler.umd.js';
    
  190.       default:
    
  191.         // For other bundles, use the shared NPM package.
    
  192.         return null;
    
  193.     }
    
  194.   },
    
  195. 
    
  196.   './packages/scheduler/src/SchedulerFeatureFlags.js': (
    
  197.     bundleType,
    
  198.     entry,
    
  199.     dependencies
    
  200.   ) => {
    
  201.     if (
    
  202.       bundleType === FB_WWW_DEV ||
    
  203.       bundleType === FB_WWW_PROD ||
    
  204.       bundleType === FB_WWW_PROFILING
    
  205.     ) {
    
  206.       return './packages/scheduler/src/forks/SchedulerFeatureFlags.www.js';
    
  207.     }
    
  208.     return './packages/scheduler/src/SchedulerFeatureFlags.js';
    
  209.   },
    
  210. 
    
  211.   './packages/shared/consoleWithStackDev.js': (bundleType, entry) => {
    
  212.     switch (bundleType) {
    
  213.       case FB_WWW_DEV:
    
  214.         return './packages/shared/forks/consoleWithStackDev.www.js';
    
  215.       default:
    
  216.         return null;
    
  217.     }
    
  218.   },
    
  219. 
    
  220.   './packages/react/src/ReactSharedInternals.js': (bundleType, entry) => {
    
  221.     switch (bundleType) {
    
  222.       case UMD_DEV:
    
  223.       case UMD_PROD:
    
  224.       case UMD_PROFILING:
    
  225.         return './packages/react/src/forks/ReactSharedInternals.umd.js';
    
  226.       default:
    
  227.         return null;
    
  228.     }
    
  229.   },
    
  230. 
    
  231.   // Different wrapping/reporting for caught errors.
    
  232.   './packages/shared/invokeGuardedCallbackImpl.js': (bundleType, entry) => {
    
  233.     switch (bundleType) {
    
  234.       case FB_WWW_DEV:
    
  235.       case FB_WWW_PROD:
    
  236.       case FB_WWW_PROFILING:
    
  237.         return './packages/shared/forks/invokeGuardedCallbackImpl.www.js';
    
  238.       default:
    
  239.         return null;
    
  240.     }
    
  241.   },
    
  242. 
    
  243.   // Different dialogs for caught errors.
    
  244.   './packages/react-reconciler/src/ReactFiberErrorDialog.js': (
    
  245.     bundleType,
    
  246.     entry
    
  247.   ) => {
    
  248.     switch (bundleType) {
    
  249.       case FB_WWW_DEV:
    
  250.       case FB_WWW_PROD:
    
  251.       case FB_WWW_PROFILING:
    
  252.         // Use the www fork which shows an error dialog.
    
  253.         return './packages/react-reconciler/src/forks/ReactFiberErrorDialog.www.js';
    
  254.       case RN_OSS_DEV:
    
  255.       case RN_OSS_PROD:
    
  256.       case RN_OSS_PROFILING:
    
  257.       case RN_FB_DEV:
    
  258.       case RN_FB_PROD:
    
  259.       case RN_FB_PROFILING:
    
  260.         switch (entry) {
    
  261.           case 'react-native-renderer':
    
  262.           case 'react-native-renderer/fabric':
    
  263.             // Use the RN fork which plays well with redbox.
    
  264.             return './packages/react-reconciler/src/forks/ReactFiberErrorDialog.native.js';
    
  265.           default:
    
  266.             return null;
    
  267.         }
    
  268.       default:
    
  269.         return null;
    
  270.     }
    
  271.   },
    
  272. 
    
  273.   './packages/react-reconciler/src/ReactFiberConfig.js': (
    
  274.     bundleType,
    
  275.     entry,
    
  276.     dependencies,
    
  277.     moduleType
    
  278.   ) => {
    
  279.     if (dependencies.indexOf('react-reconciler') !== -1) {
    
  280.       return null;
    
  281.     }
    
  282.     if (moduleType !== RENDERER && moduleType !== RECONCILER) {
    
  283.       return null;
    
  284.     }
    
  285.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  286.     for (let rendererInfo of inlinedHostConfigs) {
    
  287.       if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
    
  288.         const foundFork = findNearestExistingForkFile(
    
  289.           './packages/react-reconciler/src/forks/ReactFiberConfig.',
    
  290.           rendererInfo.shortName,
    
  291.           '.js'
    
  292.         );
    
  293.         if (foundFork) {
    
  294.           return foundFork;
    
  295.         }
    
  296.         // fall through to error
    
  297.         break;
    
  298.       }
    
  299.     }
    
  300.     throw new Error(
    
  301.       'Expected ReactFiberConfig to always be replaced with a shim, but ' +
    
  302.         `found no mention of "${entry}" entry point in ./scripts/shared/inlinedHostConfigs.js. ` +
    
  303.         'Did you mean to add it there to associate it with a specific renderer?'
    
  304.     );
    
  305.   },
    
  306. 
    
  307.   './packages/react-server/src/ReactServerStreamConfig.js': (
    
  308.     bundleType,
    
  309.     entry,
    
  310.     dependencies,
    
  311.     moduleType
    
  312.   ) => {
    
  313.     if (dependencies.indexOf('react-server') !== -1) {
    
  314.       return null;
    
  315.     }
    
  316.     if (moduleType !== RENDERER && moduleType !== RECONCILER) {
    
  317.       return null;
    
  318.     }
    
  319.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  320.     for (let rendererInfo of inlinedHostConfigs) {
    
  321.       if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
    
  322.         if (!rendererInfo.isServerSupported) {
    
  323.           return null;
    
  324.         }
    
  325.         const foundFork = findNearestExistingForkFile(
    
  326.           './packages/react-server/src/forks/ReactServerStreamConfig.',
    
  327.           rendererInfo.shortName,
    
  328.           '.js'
    
  329.         );
    
  330.         if (foundFork) {
    
  331.           return foundFork;
    
  332.         }
    
  333.         // fall through to error
    
  334.         break;
    
  335.       }
    
  336.     }
    
  337.     throw new Error(
    
  338.       'Expected ReactServerStreamConfig to always be replaced with a shim, but ' +
    
  339.         `found no mention of "${entry}" entry point in ./scripts/shared/inlinedHostConfigs.js. ` +
    
  340.         'Did you mean to add it there to associate it with a specific renderer?'
    
  341.     );
    
  342.   },
    
  343. 
    
  344.   './packages/react-server/src/ReactFizzConfig.js': (
    
  345.     bundleType,
    
  346.     entry,
    
  347.     dependencies,
    
  348.     moduleType
    
  349.   ) => {
    
  350.     if (dependencies.indexOf('react-server') !== -1) {
    
  351.       return null;
    
  352.     }
    
  353.     if (moduleType !== RENDERER && moduleType !== RECONCILER) {
    
  354.       return null;
    
  355.     }
    
  356.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  357.     for (let rendererInfo of inlinedHostConfigs) {
    
  358.       if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
    
  359.         if (!rendererInfo.isServerSupported) {
    
  360.           return null;
    
  361.         }
    
  362.         const foundFork = findNearestExistingForkFile(
    
  363.           './packages/react-server/src/forks/ReactFizzConfig.',
    
  364.           rendererInfo.shortName,
    
  365.           '.js'
    
  366.         );
    
  367.         if (foundFork) {
    
  368.           return foundFork;
    
  369.         }
    
  370.         // fall through to error
    
  371.         break;
    
  372.       }
    
  373.     }
    
  374.     throw new Error(
    
  375.       'Expected ReactFizzConfig to always be replaced with a shim, but ' +
    
  376.         `found no mention of "${entry}" entry point in ./scripts/shared/inlinedHostConfigs.js. ` +
    
  377.         'Did you mean to add it there to associate it with a specific renderer?'
    
  378.     );
    
  379.   },
    
  380. 
    
  381.   './packages/react-server/src/ReactFlightServerConfig.js': (
    
  382.     bundleType,
    
  383.     entry,
    
  384.     dependencies,
    
  385.     moduleType
    
  386.   ) => {
    
  387.     if (dependencies.indexOf('react-server') !== -1) {
    
  388.       return null;
    
  389.     }
    
  390.     if (moduleType !== RENDERER && moduleType !== RECONCILER) {
    
  391.       return null;
    
  392.     }
    
  393.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  394.     for (let rendererInfo of inlinedHostConfigs) {
    
  395.       if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
    
  396.         if (!rendererInfo.isServerSupported) {
    
  397.           return null;
    
  398.         }
    
  399.         if (rendererInfo.isFlightSupported === false) {
    
  400.           return new Error(
    
  401.             `Expected not to use ReactFlightServerConfig with "${entry}" entry point ` +
    
  402.               'in ./scripts/shared/inlinedHostConfigs.js. Update the renderer config to ' +
    
  403.               'activate flight suppport and add a matching fork implementation for ReactFlightServerConfig.'
    
  404.           );
    
  405.         }
    
  406.         const foundFork = findNearestExistingForkFile(
    
  407.           './packages/react-server/src/forks/ReactFlightServerConfig.',
    
  408.           rendererInfo.shortName,
    
  409.           '.js'
    
  410.         );
    
  411.         if (foundFork) {
    
  412.           return foundFork;
    
  413.         }
    
  414.         // fall through to error
    
  415.         break;
    
  416.       }
    
  417.     }
    
  418.     throw new Error(
    
  419.       'Expected ReactFlightServerConfig to always be replaced with a shim, but ' +
    
  420.         `found no mention of "${entry}" entry point in ./scripts/shared/inlinedHostConfigs.js. ` +
    
  421.         'Did you mean to add it there to associate it with a specific renderer?'
    
  422.     );
    
  423.   },
    
  424. 
    
  425.   './packages/react-client/src/ReactFlightClientConfig.js': (
    
  426.     bundleType,
    
  427.     entry,
    
  428.     dependencies,
    
  429.     moduleType
    
  430.   ) => {
    
  431.     if (dependencies.indexOf('react-client') !== -1) {
    
  432.       return null;
    
  433.     }
    
  434.     if (moduleType !== RENDERER && moduleType !== RECONCILER) {
    
  435.       return null;
    
  436.     }
    
  437.     // eslint-disable-next-line no-for-of-loops/no-for-of-loops
    
  438.     for (let rendererInfo of inlinedHostConfigs) {
    
  439.       if (rendererInfo.entryPoints.indexOf(entry) !== -1) {
    
  440.         if (!rendererInfo.isServerSupported) {
    
  441.           return null;
    
  442.         }
    
  443.         if (rendererInfo.isFlightSupported === false) {
    
  444.           return new Error(
    
  445.             `Expected not to use ReactFlightClientConfig with "${entry}" entry point ` +
    
  446.               'in ./scripts/shared/inlinedHostConfigs.js. Update the renderer config to ' +
    
  447.               'activate flight suppport and add a matching fork implementation for ReactFlightClientConfig.'
    
  448.           );
    
  449.         }
    
  450.         const foundFork = findNearestExistingForkFile(
    
  451.           './packages/react-client/src/forks/ReactFlightClientConfig.',
    
  452.           rendererInfo.shortName,
    
  453.           '.js'
    
  454.         );
    
  455.         if (foundFork) {
    
  456.           return foundFork;
    
  457.         }
    
  458.         // fall through to error
    
  459.         break;
    
  460.       }
    
  461.     }
    
  462.     throw new Error(
    
  463.       'Expected ReactFlightClientConfig to always be replaced with a shim, but ' +
    
  464.         `found no mention of "${entry}" entry point in ./scripts/shared/inlinedHostConfigs.js. ` +
    
  465.         'Did you mean to add it there to associate it with a specific renderer?'
    
  466.     );
    
  467.   },
    
  468. 
    
  469.   // We wrap top-level listeners into guards on www.
    
  470.   './packages/react-dom-bindings/src/events/EventListener.js': (
    
  471.     bundleType,
    
  472.     entry
    
  473.   ) => {
    
  474.     switch (bundleType) {
    
  475.       case FB_WWW_DEV:
    
  476.       case FB_WWW_PROD:
    
  477.       case FB_WWW_PROFILING:
    
  478.         if (__EXPERIMENTAL__) {
    
  479.           // In modern builds we don't use the indirection. We just use raw DOM.
    
  480.           return null;
    
  481.         } else {
    
  482.           // Use the www fork which is integrated with TimeSlice profiling.
    
  483.           return './packages/react-dom-bindings/src/events/forks/EventListener-www.js';
    
  484.         }
    
  485.       default:
    
  486.         return null;
    
  487.     }
    
  488.   },
    
  489. 
    
  490.   './packages/use-sync-external-store/src/useSyncExternalStore.js': (
    
  491.     bundleType,
    
  492.     entry
    
  493.   ) => {
    
  494.     if (entry.startsWith('use-sync-external-store/shim')) {
    
  495.       return './packages/use-sync-external-store/src/forks/useSyncExternalStore.forward-to-shim.js';
    
  496.     }
    
  497.     if (entry !== 'use-sync-external-store') {
    
  498.       // Internal modules that aren't shims should use the native API from the
    
  499.       // react package.
    
  500.       return './packages/use-sync-external-store/src/forks/useSyncExternalStore.forward-to-built-in.js';
    
  501.     }
    
  502.     return null;
    
  503.   },
    
  504. 
    
  505.   './packages/use-sync-external-store/src/isServerEnvironment.js': (
    
  506.     bundleType,
    
  507.     entry
    
  508.   ) => {
    
  509.     if (entry.endsWith('.native')) {
    
  510.       return './packages/use-sync-external-store/src/forks/isServerEnvironment.native.js';
    
  511.     }
    
  512.   },
    
  513. });
    
  514. 
    
  515. module.exports = forks;