1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  */
    
  7. 
    
  8. 'use strict';
    
  9. 
    
  10. const chalk = require('chalk');
    
  11. const fs = require('fs');
    
  12. const path = require('path');
    
  13. const mkdirp = require('mkdirp');
    
  14. const inlinedHostConfigs = require('../shared/inlinedHostConfigs');
    
  15. 
    
  16. const configTemplate = fs
    
  17.   .readFileSync(__dirname + '/config/flowconfig')
    
  18.   .toString();
    
  19. 
    
  20. // stores all forks discovered during config generation
    
  21. const allForks = new Set();
    
  22. // maps forked file to the base path containing it and it's forks (it's parent)
    
  23. const forkedFiles = new Map();
    
  24. 
    
  25. function findForks(file) {
    
  26.   const basePath = path.join(file, '..');
    
  27.   const forksPath = path.join(basePath, 'forks');
    
  28.   const forks = fs.readdirSync(path.join('packages', forksPath));
    
  29.   forks.forEach(f => allForks.add('forks/' + f));
    
  30.   forkedFiles.set(file, basePath);
    
  31.   return basePath;
    
  32. }
    
  33. 
    
  34. function addFork(forks, renderer, file) {
    
  35.   let basePath = forkedFiles.get(file);
    
  36.   if (!basePath) {
    
  37.     basePath = findForks(file);
    
  38.   }
    
  39. 
    
  40.   const baseFilename = file.slice(basePath.length + 1);
    
  41. 
    
  42.   const parts = renderer.split('-');
    
  43.   while (parts.length) {
    
  44.     const candidate = `forks/${baseFilename}.${parts.join('-')}.js`;
    
  45.     if (allForks.has(candidate)) {
    
  46.       forks.set(candidate, `${baseFilename}$$`);
    
  47.       return;
    
  48.     }
    
  49.     parts.pop();
    
  50.   }
    
  51.   throw new Error(`Cannot find fork for ${file} for renderer ${renderer}`);
    
  52. }
    
  53. 
    
  54. function writeConfig(
    
  55.   renderer,
    
  56.   rendererInfo,
    
  57.   isServerSupported,
    
  58.   isFlightSupported,
    
  59. ) {
    
  60.   const folder = __dirname + '/' + renderer;
    
  61.   mkdirp.sync(folder);
    
  62. 
    
  63.   isFlightSupported =
    
  64.     isFlightSupported === true ||
    
  65.     (isServerSupported && isFlightSupported !== false);
    
  66. 
    
  67.   const serverRenderer = isServerSupported ? renderer : 'custom';
    
  68.   const flightRenderer = isFlightSupported ? renderer : 'custom';
    
  69. 
    
  70.   const ignoredPaths = [];
    
  71. 
    
  72.   inlinedHostConfigs.forEach(otherRenderer => {
    
  73.     if (otherRenderer === rendererInfo) {
    
  74.       return;
    
  75.     }
    
  76.     otherRenderer.paths.forEach(otherPath => {
    
  77.       if (rendererInfo.paths.indexOf(otherPath) !== -1) {
    
  78.         return;
    
  79.       }
    
  80.       ignoredPaths.push(`.*/packages/${otherPath}`);
    
  81.     });
    
  82.   });
    
  83. 
    
  84.   const forks = new Map();
    
  85.   addFork(forks, renderer, 'react-reconciler/src/ReactFiberConfig');
    
  86.   addFork(forks, serverRenderer, 'react-server/src/ReactServerStreamConfig');
    
  87.   addFork(forks, serverRenderer, 'react-server/src/ReactFizzConfig');
    
  88.   addFork(forks, flightRenderer, 'react-server/src/ReactFlightServerConfig');
    
  89.   addFork(forks, flightRenderer, 'react-client/src/ReactFlightClientConfig');
    
  90.   forks.set(
    
  91.     'react-devtools-shared/src/config/DevToolsFeatureFlags.default',
    
  92.     'react-devtools-feature-flags',
    
  93.   );
    
  94. 
    
  95.   allForks.forEach(fork => {
    
  96.     if (!forks.has(fork)) {
    
  97.       ignoredPaths.push(`.*/packages/.*/${fork}`);
    
  98.     }
    
  99.   });
    
  100. 
    
  101.   let moduleMappings = '';
    
  102.   forks.forEach((source, target) => {
    
  103.     moduleMappings += `module.name_mapper='${source.slice(
    
  104.       source.lastIndexOf('/') + 1,
    
  105.     )}' -> '${target}'\n`;
    
  106.   });
    
  107. 
    
  108.   const config = configTemplate
    
  109.     .replace(
    
  110.       '%CI_MAX_WORKERS%\n',
    
  111.       // On CI, we seem to need to limit workers.
    
  112.       process.env.CI ? 'server.max_workers=4\n' : '',
    
  113.     )
    
  114.     .replace('%REACT_RENDERER_FLOW_OPTIONS%', moduleMappings.trim())
    
  115.     .replace('%REACT_RENDERER_FLOW_IGNORES%', ignoredPaths.join('\n'));
    
  116. 
    
  117.   const disclaimer = `
    
  118. # ---------------------------------------------------------------#
    
  119. # NOTE: this file is generated.                                  #
    
  120. # If you want to edit it, open ./scripts/flow/config/flowconfig. #
    
  121. # Then run Yarn for changes to take effect.                      #
    
  122. # ---------------------------------------------------------------#
    
  123.   `.trim();
    
  124. 
    
  125.   const configFile = folder + '/.flowconfig';
    
  126.   let oldConfig;
    
  127.   try {
    
  128.     oldConfig = fs.readFileSync(configFile).toString();
    
  129.   } catch (err) {
    
  130.     oldConfig = null;
    
  131.   }
    
  132.   const newConfig = `
    
  133. ${disclaimer}
    
  134. ${config}
    
  135. ${disclaimer}
    
  136. `.trim();
    
  137. 
    
  138.   if (newConfig !== oldConfig) {
    
  139.     fs.writeFileSync(configFile, newConfig);
    
  140.     console.log(chalk.dim('Wrote a Flow config to ' + configFile));
    
  141.   }
    
  142. }
    
  143. 
    
  144. // Write multiple configs in different folders
    
  145. // so that we can run those checks in parallel if we want.
    
  146. inlinedHostConfigs.forEach(rendererInfo => {
    
  147.   if (rendererInfo.isFlowTyped) {
    
  148.     writeConfig(
    
  149.       rendererInfo.shortName,
    
  150.       rendererInfo,
    
  151.       rendererInfo.isServerSupported,
    
  152.       rendererInfo.isFlightSupported,
    
  153.     );
    
  154.   }
    
  155. });