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.  * @format
    
  8.  * @flow strict-local
    
  9.  */
    
  10. 
    
  11. 'use strict';
    
  12. 
    
  13. import {type ViewConfig} from './ReactNativeTypes';
    
  14. import invariant from 'invariant';
    
  15. 
    
  16. // Event configs
    
  17. export const customBubblingEventTypes: {
    
  18.   [eventName: string]: $ReadOnly<{
    
  19.     phasedRegistrationNames: $ReadOnly<{
    
  20.       captured: string,
    
  21.       bubbled: string,
    
  22.       skipBubbling?: ?boolean,
    
  23.     }>,
    
  24.   }>,
    
  25.   ...
    
  26. } = {};
    
  27. export const customDirectEventTypes: {
    
  28.   [eventName: string]: $ReadOnly<{
    
  29.     registrationName: string,
    
  30.   }>,
    
  31.   ...
    
  32. } = {};
    
  33. 
    
  34. const viewConfigCallbacks = new Map<string, ?() => ViewConfig>();
    
  35. const viewConfigs = new Map<string, ViewConfig>();
    
  36. 
    
  37. function processEventTypes(viewConfig: ViewConfig): void {
    
  38.   const {bubblingEventTypes, directEventTypes} = viewConfig;
    
  39. 
    
  40.   if (__DEV__) {
    
  41.     if (bubblingEventTypes != null && directEventTypes != null) {
    
  42.       for (const topLevelType in directEventTypes) {
    
  43.         invariant(
    
  44.           bubblingEventTypes[topLevelType] == null,
    
  45.           'Event cannot be both direct and bubbling: %s',
    
  46.           topLevelType,
    
  47.         );
    
  48.       }
    
  49.     }
    
  50.   }
    
  51. 
    
  52.   if (bubblingEventTypes != null) {
    
  53.     for (const topLevelType in bubblingEventTypes) {
    
  54.       if (customBubblingEventTypes[topLevelType] == null) {
    
  55.         customBubblingEventTypes[topLevelType] =
    
  56.           bubblingEventTypes[topLevelType];
    
  57.       }
    
  58.     }
    
  59.   }
    
  60. 
    
  61.   if (directEventTypes != null) {
    
  62.     for (const topLevelType in directEventTypes) {
    
  63.       if (customDirectEventTypes[topLevelType] == null) {
    
  64.         customDirectEventTypes[topLevelType] = directEventTypes[topLevelType];
    
  65.       }
    
  66.     }
    
  67.   }
    
  68. }
    
  69. 
    
  70. /**
    
  71.  * Registers a native view/component by name.
    
  72.  * A callback is provided to load the view config from UIManager.
    
  73.  * The callback is deferred until the view is actually rendered.
    
  74.  */
    
  75. export function register(name: string, callback: () => ViewConfig): string {
    
  76.   invariant(
    
  77.     !viewConfigCallbacks.has(name),
    
  78.     'Tried to register two views with the same name %s',
    
  79.     name,
    
  80.   );
    
  81.   invariant(
    
  82.     typeof callback === 'function',
    
  83.     'View config getter callback for component `%s` must be a function (received `%s`)',
    
  84.     name,
    
  85.     callback === null ? 'null' : typeof callback,
    
  86.   );
    
  87.   viewConfigCallbacks.set(name, callback);
    
  88.   return name;
    
  89. }
    
  90. 
    
  91. /**
    
  92.  * Retrieves a config for the specified view.
    
  93.  * If this is the first time the view has been used,
    
  94.  * This configuration will be lazy-loaded from UIManager.
    
  95.  */
    
  96. export function get(name: string): ViewConfig {
    
  97.   let viewConfig;
    
  98.   if (!viewConfigs.has(name)) {
    
  99.     const callback = viewConfigCallbacks.get(name);
    
  100.     if (typeof callback !== 'function') {
    
  101.       invariant(
    
  102.         false,
    
  103.         'View config getter callback for component `%s` must be a function (received `%s`).%s',
    
  104.         name,
    
  105.         callback === null ? 'null' : typeof callback,
    
  106.         // $FlowFixMe[recursive-definition]
    
  107.         typeof name[0] === 'string' && /[a-z]/.test(name[0])
    
  108.           ? ' Make sure to start component names with a capital letter.'
    
  109.           : '',
    
  110.       );
    
  111.     }
    
  112.     viewConfig = callback();
    
  113.     processEventTypes(viewConfig);
    
  114.     viewConfigs.set(name, viewConfig);
    
  115. 
    
  116.     // Clear the callback after the config is set so that
    
  117.     // we don't mask any errors during registration.
    
  118.     viewConfigCallbacks.set(name, null);
    
  119.   } else {
    
  120.     viewConfig = viewConfigs.get(name);
    
  121.   }
    
  122.   invariant(viewConfig, 'View config not found for name %s', name);
    
  123.   return viewConfig;
    
  124. }