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.  * @flow
    
  8.  */
    
  9. import type {
    
  10.   PreconnectOptions,
    
  11.   PreloadOptions,
    
  12.   PreloadModuleOptions,
    
  13.   PreinitOptions,
    
  14.   PreinitModuleOptions,
    
  15. } from './ReactDOMTypes';
    
  16. 
    
  17. import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
    
  18. const Dispatcher = ReactDOMSharedInternals.Dispatcher;
    
  19. 
    
  20. import {
    
  21.   getCrossOriginString,
    
  22.   getCrossOriginStringAs,
    
  23. } from 'react-dom-bindings/src/shared/crossOriginStrings';
    
  24. 
    
  25. export function prefetchDNS(href: string) {
    
  26.   if (__DEV__) {
    
  27.     if (typeof href !== 'string' || !href) {
    
  28.       console.error(
    
  29.         'ReactDOM.prefetchDNS(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
    
  30.         getValueDescriptorExpectingObjectForWarning(href),
    
  31.       );
    
  32.     } else if (arguments.length > 1) {
    
  33.       const options = arguments[1];
    
  34.       if (
    
  35.         typeof options === 'object' &&
    
  36.         options.hasOwnProperty('crossOrigin')
    
  37.       ) {
    
  38.         console.error(
    
  39.           'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. It looks like the you are attempting to set a crossOrigin property for this DNS lookup hint. Browsers do not perform DNS queries using CORS and setting this attribute on the resource hint has no effect. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
    
  40.           getValueDescriptorExpectingEnumForWarning(options),
    
  41.         );
    
  42.       } else {
    
  43.         console.error(
    
  44.           'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
    
  45.           getValueDescriptorExpectingEnumForWarning(options),
    
  46.         );
    
  47.       }
    
  48.     }
    
  49.   }
    
  50.   const dispatcher = Dispatcher.current;
    
  51.   if (dispatcher && typeof href === 'string') {
    
  52.     dispatcher.prefetchDNS(href);
    
  53.   }
    
  54.   // We don't error because preconnect needs to be resilient to being called in a variety of scopes
    
  55.   // and the runtime may not be capable of responding. The function is optimistic and not critical
    
  56.   // so we favor silent bailout over warning or erroring.
    
  57. }
    
  58. 
    
  59. export function preconnect(href: string, options?: ?PreconnectOptions) {
    
  60.   if (__DEV__) {
    
  61.     if (typeof href !== 'string' || !href) {
    
  62.       console.error(
    
  63.         'ReactDOM.preconnect(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
    
  64.         getValueDescriptorExpectingObjectForWarning(href),
    
  65.       );
    
  66.     } else if (options != null && typeof options !== 'object') {
    
  67.       console.error(
    
  68.         'ReactDOM.preconnect(): Expected the `options` argument (second) to be an object but encountered %s instead. The only supported option at this time is `crossOrigin` which accepts a string.',
    
  69.         getValueDescriptorExpectingEnumForWarning(options),
    
  70.       );
    
  71.     } else if (options != null && typeof options.crossOrigin !== 'string') {
    
  72.       console.error(
    
  73.         'ReactDOM.preconnect(): Expected the `crossOrigin` option (second argument) to be a string but encountered %s instead. Try removing this option or passing a string value instead.',
    
  74.         getValueDescriptorExpectingObjectForWarning(options.crossOrigin),
    
  75.       );
    
  76.     }
    
  77.   }
    
  78.   const dispatcher = Dispatcher.current;
    
  79.   if (dispatcher && typeof href === 'string') {
    
  80.     const crossOrigin = options
    
  81.       ? getCrossOriginString(options.crossOrigin)
    
  82.       : null;
    
  83.     dispatcher.preconnect(href, crossOrigin);
    
  84.   }
    
  85.   // We don't error because preconnect needs to be resilient to being called in a variety of scopes
    
  86.   // and the runtime may not be capable of responding. The function is optimistic and not critical
    
  87.   // so we favor silent bailout over warning or erroring.
    
  88. }
    
  89. 
    
  90. export function preload(href: string, options: PreloadOptions) {
    
  91.   if (__DEV__) {
    
  92.     let encountered = '';
    
  93.     if (typeof href !== 'string' || !href) {
    
  94.       encountered += ` The \`href\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  95.         href,
    
  96.       )}.`;
    
  97.     }
    
  98.     if (options == null || typeof options !== 'object') {
    
  99.       encountered += ` The \`options\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  100.         options,
    
  101.       )}.`;
    
  102.     } else if (typeof options.as !== 'string' || !options.as) {
    
  103.       encountered += ` The \`as\` option encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  104.         options.as,
    
  105.       )}.`;
    
  106.     }
    
  107.     if (encountered) {
    
  108.       console.error(
    
  109.         'ReactDOM.preload(): Expected two arguments, a non-empty `href` string and an `options` object with an `as` property valid for a `<link rel="preload" as="..." />` tag.%s',
    
  110.         encountered,
    
  111.       );
    
  112.     }
    
  113.   }
    
  114.   const dispatcher = Dispatcher.current;
    
  115.   if (
    
  116.     dispatcher &&
    
  117.     typeof href === 'string' &&
    
  118.     // We check existence because we cannot enforce this function is actually called with the stated type
    
  119.     typeof options === 'object' &&
    
  120.     options !== null &&
    
  121.     typeof options.as === 'string'
    
  122.   ) {
    
  123.     const as = options.as;
    
  124.     const crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);
    
  125.     dispatcher.preload(href, as, {
    
  126.       crossOrigin,
    
  127.       integrity:
    
  128.         typeof options.integrity === 'string' ? options.integrity : undefined,
    
  129.       nonce: typeof options.nonce === 'string' ? options.nonce : undefined,
    
  130.       type: typeof options.type === 'string' ? options.type : undefined,
    
  131.       fetchPriority:
    
  132.         typeof options.fetchPriority === 'string'
    
  133.           ? options.fetchPriority
    
  134.           : undefined,
    
  135.       referrerPolicy:
    
  136.         typeof options.referrerPolicy === 'string'
    
  137.           ? options.referrerPolicy
    
  138.           : undefined,
    
  139.       imageSrcSet:
    
  140.         typeof options.imageSrcSet === 'string'
    
  141.           ? options.imageSrcSet
    
  142.           : undefined,
    
  143.       imageSizes:
    
  144.         typeof options.imageSizes === 'string' ? options.imageSizes : undefined,
    
  145.     });
    
  146.   }
    
  147.   // We don't error because preload needs to be resilient to being called in a variety of scopes
    
  148.   // and the runtime may not be capable of responding. The function is optimistic and not critical
    
  149.   // so we favor silent bailout over warning or erroring.
    
  150. }
    
  151. 
    
  152. export function preloadModule(href: string, options?: ?PreloadModuleOptions) {
    
  153.   if (__DEV__) {
    
  154.     let encountered = '';
    
  155.     if (typeof href !== 'string' || !href) {
    
  156.       encountered += ` The \`href\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  157.         href,
    
  158.       )}.`;
    
  159.     }
    
  160.     if (options !== undefined && typeof options !== 'object') {
    
  161.       encountered += ` The \`options\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  162.         options,
    
  163.       )}.`;
    
  164.     } else if (options && 'as' in options && typeof options.as !== 'string') {
    
  165.       encountered += ` The \`as\` option encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  166.         options.as,
    
  167.       )}.`;
    
  168.     }
    
  169.     if (encountered) {
    
  170.       console.error(
    
  171.         'ReactDOM.preloadModule(): Expected two arguments, a non-empty `href` string and, optionally, an `options` object with an `as` property valid for a `<link rel="modulepreload" as="..." />` tag.%s',
    
  172.         encountered,
    
  173.       );
    
  174.     }
    
  175.   }
    
  176.   const dispatcher = Dispatcher.current;
    
  177.   if (dispatcher && typeof href === 'string') {
    
  178.     if (options) {
    
  179.       const crossOrigin = getCrossOriginStringAs(
    
  180.         options.as,
    
  181.         options.crossOrigin,
    
  182.       );
    
  183.       dispatcher.preloadModule(href, {
    
  184.         as:
    
  185.           typeof options.as === 'string' && options.as !== 'script'
    
  186.             ? options.as
    
  187.             : undefined,
    
  188.         crossOrigin,
    
  189.         integrity:
    
  190.           typeof options.integrity === 'string' ? options.integrity : undefined,
    
  191.       });
    
  192.     } else {
    
  193.       dispatcher.preloadModule(href);
    
  194.     }
    
  195.   }
    
  196.   // We don't error because preload needs to be resilient to being called in a variety of scopes
    
  197.   // and the runtime may not be capable of responding. The function is optimistic and not critical
    
  198.   // so we favor silent bailout over warning or erroring.
    
  199. }
    
  200. 
    
  201. export function preinit(href: string, options: PreinitOptions) {
    
  202.   if (__DEV__) {
    
  203.     if (typeof href !== 'string' || !href) {
    
  204.       console.error(
    
  205.         'ReactDOM.preinit(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
    
  206.         getValueDescriptorExpectingObjectForWarning(href),
    
  207.       );
    
  208.     } else if (options == null || typeof options !== 'object') {
    
  209.       console.error(
    
  210.         'ReactDOM.preinit(): Expected the `options` argument (second) to be an object with an `as` property describing the type of resource to be preinitialized but encountered %s instead.',
    
  211.         getValueDescriptorExpectingEnumForWarning(options),
    
  212.       );
    
  213.     } else if (options.as !== 'style' && options.as !== 'script') {
    
  214.       console.error(
    
  215.         'ReactDOM.preinit(): Expected the `as` property in the `options` argument (second) to contain a valid value describing the type of resource to be preinitialized but encountered %s instead. Valid values for `as` are "style" and "script".',
    
  216.         getValueDescriptorExpectingEnumForWarning(options.as),
    
  217.       );
    
  218.     }
    
  219.   }
    
  220.   const dispatcher = Dispatcher.current;
    
  221.   if (
    
  222.     dispatcher &&
    
  223.     typeof href === 'string' &&
    
  224.     options &&
    
  225.     typeof options.as === 'string'
    
  226.   ) {
    
  227.     const as = options.as;
    
  228.     const crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);
    
  229.     const integrity =
    
  230.       typeof options.integrity === 'string' ? options.integrity : undefined;
    
  231.     const fetchPriority =
    
  232.       typeof options.fetchPriority === 'string'
    
  233.         ? options.fetchPriority
    
  234.         : undefined;
    
  235.     if (as === 'style') {
    
  236.       dispatcher.preinitStyle(
    
  237.         href,
    
  238.         typeof options.precedence === 'string' ? options.precedence : undefined,
    
  239.         {
    
  240.           crossOrigin,
    
  241.           integrity,
    
  242.           fetchPriority,
    
  243.         },
    
  244.       );
    
  245.     } else if (as === 'script') {
    
  246.       dispatcher.preinitScript(href, {
    
  247.         crossOrigin,
    
  248.         integrity,
    
  249.         fetchPriority,
    
  250.         nonce: typeof options.nonce === 'string' ? options.nonce : undefined,
    
  251.       });
    
  252.     }
    
  253.   }
    
  254.   // We don't error because preinit needs to be resilient to being called in a variety of scopes
    
  255.   // and the runtime may not be capable of responding. The function is optimistic and not critical
    
  256.   // so we favor silent bailout over warning or erroring.
    
  257. }
    
  258. 
    
  259. export function preinitModule(href: string, options?: ?PreinitModuleOptions) {
    
  260.   if (__DEV__) {
    
  261.     let encountered = '';
    
  262.     if (typeof href !== 'string' || !href) {
    
  263.       encountered += ` The \`href\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  264.         href,
    
  265.       )}.`;
    
  266.     }
    
  267.     if (options !== undefined && typeof options !== 'object') {
    
  268.       encountered += ` The \`options\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
    
  269.         options,
    
  270.       )}.`;
    
  271.     } else if (options && 'as' in options && options.as !== 'script') {
    
  272.       encountered += ` The \`as\` option encountered was ${getValueDescriptorExpectingEnumForWarning(
    
  273.         options.as,
    
  274.       )}.`;
    
  275.     }
    
  276.     if (encountered) {
    
  277.       console.error(
    
  278.         'ReactDOM.preinitModule(): Expected up to two arguments, a non-empty `href` string and, optionally, an `options` object with a valid `as` property.%s',
    
  279.         encountered,
    
  280.       );
    
  281.     } else {
    
  282.       const as =
    
  283.         options && typeof options.as === 'string' ? options.as : 'script';
    
  284.       switch (as) {
    
  285.         case 'script': {
    
  286.           break;
    
  287.         }
    
  288. 
    
  289.         // We have an invalid as type and need to warn
    
  290.         default: {
    
  291.           const typeOfAs = getValueDescriptorExpectingEnumForWarning(as);
    
  292.           console.error(
    
  293.             'ReactDOM.preinitModule(): Currently the only supported "as" type for this function is "script"' +
    
  294.               ' but received "%s" instead. This warning was generated for `href` "%s". In the future other' +
    
  295.               ' module types will be supported, aligning with the import-attributes proposal. Learn more here:' +
    
  296.               ' (https://github.com/tc39/proposal-import-attributes)',
    
  297.             typeOfAs,
    
  298.             href,
    
  299.           );
    
  300.         }
    
  301.       }
    
  302.     }
    
  303.   }
    
  304.   const dispatcher = Dispatcher.current;
    
  305.   if (dispatcher && typeof href === 'string') {
    
  306.     if (typeof options === 'object' && options !== null) {
    
  307.       if (options.as == null || options.as === 'script') {
    
  308.         const crossOrigin = getCrossOriginStringAs(
    
  309.           options.as,
    
  310.           options.crossOrigin,
    
  311.         );
    
  312.         dispatcher.preinitModuleScript(href, {
    
  313.           crossOrigin,
    
  314.           integrity:
    
  315.             typeof options.integrity === 'string'
    
  316.               ? options.integrity
    
  317.               : undefined,
    
  318.           nonce: typeof options.nonce === 'string' ? options.nonce : undefined,
    
  319.         });
    
  320.       }
    
  321.     } else if (options == null) {
    
  322.       dispatcher.preinitModuleScript(href);
    
  323.     }
    
  324.   }
    
  325.   // We don't error because preinit needs to be resilient to being called in a variety of scopes
    
  326.   // and the runtime may not be capable of responding. The function is optimistic and not critical
    
  327.   // so we favor silent bailout over warning or erroring.
    
  328. }
    
  329. 
    
  330. function getValueDescriptorExpectingObjectForWarning(thing: any): string {
    
  331.   return thing === null
    
  332.     ? '`null`'
    
  333.     : thing === undefined
    
  334.     ? '`undefined`'
    
  335.     : thing === ''
    
  336.     ? 'an empty string'
    
  337.     : `something with type "${typeof thing}"`;
    
  338. }
    
  339. 
    
  340. function getValueDescriptorExpectingEnumForWarning(thing: any): string {
    
  341.   return thing === null
    
  342.     ? '`null`'
    
  343.     : thing === undefined
    
  344.     ? '`undefined`'
    
  345.     : thing === ''
    
  346.     ? 'an empty string'
    
  347.     : typeof thing === 'string'
    
  348.     ? JSON.stringify(thing)
    
  349.     : typeof thing === 'number'
    
  350.     ? '`' + thing + '`'
    
  351.     : `something with type "${typeof thing}"`;
    
  352. }