1. /**
    
  2.  * Install the hook on window, which is an event emitter.
    
  3.  * Note: this global hook __REACT_DEVTOOLS_GLOBAL_HOOK__ is a de facto public API.
    
  4.  * It's especially important to avoid creating direct dependency on the DevTools Backend.
    
  5.  * That's why we still inline the whole event emitter implementation,
    
  6.  * the string format implementation, and part of the console implementation here.
    
  7.  *
    
  8.  * @flow
    
  9.  */
    
  10. 
    
  11. import type {BrowserTheme} from './frontend/types';
    
  12. import type {
    
  13.   DevToolsHook,
    
  14.   Handler,
    
  15.   ReactRenderer,
    
  16.   RendererID,
    
  17.   RendererInterface,
    
  18.   DevToolsBackend,
    
  19. } from './backend/types';
    
  20. 
    
  21. declare var window: any;
    
  22. 
    
  23. export function installHook(target: any): DevToolsHook | null {
    
  24.   if (target.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) {
    
  25.     return null;
    
  26.   }
    
  27. 
    
  28.   let targetConsole: Object = console;
    
  29.   let targetConsoleMethods: {[string]: $FlowFixMe} = {};
    
  30.   for (const method in console) {
    
  31.     targetConsoleMethods[method] = console[method];
    
  32.   }
    
  33. 
    
  34.   function dangerous_setTargetConsoleForTesting(
    
  35.     targetConsoleForTesting: Object,
    
  36.   ): void {
    
  37.     targetConsole = targetConsoleForTesting;
    
  38. 
    
  39.     targetConsoleMethods = ({}: {[string]: $FlowFixMe});
    
  40.     for (const method in targetConsole) {
    
  41.       targetConsoleMethods[method] = console[method];
    
  42.     }
    
  43.   }
    
  44. 
    
  45.   function detectReactBuildType(renderer: ReactRenderer) {
    
  46.     try {
    
  47.       if (typeof renderer.version === 'string') {
    
  48.         // React DOM Fiber (16+)
    
  49.         if (renderer.bundleType > 0) {
    
  50.           // This is not a production build.
    
  51.           // We are currently only using 0 (PROD) and 1 (DEV)
    
  52.           // but might add 2 (PROFILE) in the future.
    
  53.           return 'development';
    
  54.         }
    
  55. 
    
  56.         // React 16 uses flat bundles. If we report the bundle as production
    
  57.         // version, it means we also minified and envified it ourselves.
    
  58.         return 'production';
    
  59.         // Note: There is still a risk that the CommonJS entry point has not
    
  60.         // been envified or uglified. In this case the user would have *both*
    
  61.         // development and production bundle, but only the prod one would run.
    
  62.         // This would be really bad. We have a separate check for this because
    
  63.         // it happens *outside* of the renderer injection. See `checkDCE` below.
    
  64.       }
    
  65. 
    
  66.       // $FlowFixMe[method-unbinding]
    
  67.       const toString = Function.prototype.toString;
    
  68.       if (renderer.Mount && renderer.Mount._renderNewRootComponent) {
    
  69.         // React DOM Stack
    
  70.         const renderRootCode = toString.call(
    
  71.           renderer.Mount._renderNewRootComponent,
    
  72.         );
    
  73.         // Filter out bad results (if that is even possible):
    
  74.         if (renderRootCode.indexOf('function') !== 0) {
    
  75.           // Hope for the best if we're not sure.
    
  76.           return 'production';
    
  77.         }
    
  78.         // Check for React DOM Stack < 15.1.0 in development.
    
  79.         // If it contains "storedMeasure" call, it's wrapped in ReactPerf (DEV only).
    
  80.         // This would be true even if it's minified, as method name still matches.
    
  81.         if (renderRootCode.indexOf('storedMeasure') !== -1) {
    
  82.           return 'development';
    
  83.         }
    
  84.         // For other versions (and configurations) it's not so easy.
    
  85.         // Let's quickly exclude proper production builds.
    
  86.         // If it contains a warning message, it's either a DEV build,
    
  87.         // or an PROD build without proper dead code elimination.
    
  88.         if (renderRootCode.indexOf('should be a pure function') !== -1) {
    
  89.           // Now how do we tell a DEV build from a bad PROD build?
    
  90.           // If we see NODE_ENV, we're going to assume this is a dev build
    
  91.           // because most likely it is referring to an empty shim.
    
  92.           if (renderRootCode.indexOf('NODE_ENV') !== -1) {
    
  93.             return 'development';
    
  94.           }
    
  95.           // If we see "development", we're dealing with an envified DEV build
    
  96.           // (such as the official React DEV UMD).
    
  97.           if (renderRootCode.indexOf('development') !== -1) {
    
  98.             return 'development';
    
  99.           }
    
  100.           // I've seen process.env.NODE_ENV !== 'production' being smartly
    
  101.           // replaced by `true` in DEV by Webpack. I don't know how that
    
  102.           // works but we can safely guard against it because `true` was
    
  103.           // never used in the function source since it was written.
    
  104.           if (renderRootCode.indexOf('true') !== -1) {
    
  105.             return 'development';
    
  106.           }
    
  107.           // By now either it is a production build that has not been minified,
    
  108.           // or (worse) this is a minified development build using non-standard
    
  109.           // environment (e.g. "staging"). We're going to look at whether
    
  110.           // the function argument name is mangled:
    
  111.           if (
    
  112.             // 0.13 to 15
    
  113.             renderRootCode.indexOf('nextElement') !== -1 ||
    
  114.             // 0.12
    
  115.             renderRootCode.indexOf('nextComponent') !== -1
    
  116.           ) {
    
  117.             // We can't be certain whether this is a development build or not,
    
  118.             // but it is definitely unminified.
    
  119.             return 'unminified';
    
  120.           } else {
    
  121.             // This is likely a minified development build.
    
  122.             return 'development';
    
  123.           }
    
  124.         }
    
  125.         // By now we know that it's envified and dead code elimination worked,
    
  126.         // but what if it's still not minified? (Is this even possible?)
    
  127.         // Let's check matches for the first argument name.
    
  128.         if (
    
  129.           // 0.13 to 15
    
  130.           renderRootCode.indexOf('nextElement') !== -1 ||
    
  131.           // 0.12
    
  132.           renderRootCode.indexOf('nextComponent') !== -1
    
  133.         ) {
    
  134.           return 'unminified';
    
  135.         }
    
  136.         // Seems like we're using the production version.
    
  137.         // However, the branch above is Stack-only so this is 15 or earlier.
    
  138.         return 'outdated';
    
  139.       }
    
  140.     } catch (err) {
    
  141.       // Weird environments may exist.
    
  142.       // This code needs a higher fault tolerance
    
  143.       // because it runs even with closed DevTools.
    
  144.       // TODO: should we catch errors in all injected code, and not just this part?
    
  145.     }
    
  146.     return 'production';
    
  147.   }
    
  148. 
    
  149.   function checkDCE(fn: Function) {
    
  150.     // This runs for production versions of React.
    
  151.     // Needs to be super safe.
    
  152.     try {
    
  153.       // $FlowFixMe[method-unbinding]
    
  154.       const toString = Function.prototype.toString;
    
  155.       const code = toString.call(fn);
    
  156. 
    
  157.       // This is a string embedded in the passed function under DEV-only
    
  158.       // condition. However the function executes only in PROD. Therefore,
    
  159.       // if we see it, dead code elimination did not work.
    
  160.       if (code.indexOf('^_^') > -1) {
    
  161.         // Remember to report during next injection.
    
  162.         hasDetectedBadDCE = true;
    
  163. 
    
  164.         // Bonus: throw an exception hoping that it gets picked up by a reporting system.
    
  165.         // Not synchronously so that it doesn't break the calling code.
    
  166.         setTimeout(function () {
    
  167.           throw new Error(
    
  168.             'React is running in production mode, but dead code ' +
    
  169.               'elimination has not been applied. Read how to correctly ' +
    
  170.               'configure React for production: ' +
    
  171.               'https://reactjs.org/link/perf-use-production-build',
    
  172.           );
    
  173.         });
    
  174.       }
    
  175.     } catch (err) {}
    
  176.   }
    
  177. 
    
  178.   // NOTE: KEEP IN SYNC with src/backend/utils.js
    
  179.   function formatWithStyles(
    
  180.     inputArgs: $ReadOnlyArray<any>,
    
  181.     style?: string,
    
  182.   ): $ReadOnlyArray<any> {
    
  183.     if (
    
  184.       inputArgs === undefined ||
    
  185.       inputArgs === null ||
    
  186.       inputArgs.length === 0 ||
    
  187.       // Matches any of %c but not %%c
    
  188.       (typeof inputArgs[0] === 'string' &&
    
  189.         inputArgs[0].match(/([^%]|^)(%c)/g)) ||
    
  190.       style === undefined
    
  191.     ) {
    
  192.       return inputArgs;
    
  193.     }
    
  194. 
    
  195.     // Matches any of %(o|O|d|i|s|f), but not %%(o|O|d|i|s|f)
    
  196.     const REGEXP = /([^%]|^)((%%)*)(%([oOdisf]))/g;
    
  197.     if (typeof inputArgs[0] === 'string' && inputArgs[0].match(REGEXP)) {
    
  198.       return [`%c${inputArgs[0]}`, style, ...inputArgs.slice(1)];
    
  199.     } else {
    
  200.       const firstArg = inputArgs.reduce((formatStr, elem, i) => {
    
  201.         if (i > 0) {
    
  202.           formatStr += ' ';
    
  203.         }
    
  204.         switch (typeof elem) {
    
  205.           case 'string':
    
  206.           case 'boolean':
    
  207.           case 'symbol':
    
  208.             return (formatStr += '%s');
    
  209.           case 'number':
    
  210.             const formatting = Number.isInteger(elem) ? '%i' : '%f';
    
  211.             return (formatStr += formatting);
    
  212.           default:
    
  213.             return (formatStr += '%o');
    
  214.         }
    
  215.       }, '%c');
    
  216.       return [firstArg, style, ...inputArgs];
    
  217.     }
    
  218.   }
    
  219. 
    
  220.   let unpatchFn = null;
    
  221. 
    
  222.   // NOTE: KEEP IN SYNC with src/backend/console.js:patchForStrictMode
    
  223.   // This function hides or dims console logs during the initial double renderer
    
  224.   // in Strict Mode. We need this function because during initial render,
    
  225.   // React and DevTools are connecting and the renderer interface isn't avaiable
    
  226.   // and we want to be able to have consistent logging behavior for double logs
    
  227.   // during the initial renderer.
    
  228.   function patchConsoleForInitialRenderInStrictMode({
    
  229.     hideConsoleLogsInStrictMode,
    
  230.     browserTheme,
    
  231.   }: {
    
  232.     hideConsoleLogsInStrictMode: boolean,
    
  233.     browserTheme: BrowserTheme,
    
  234.   }) {
    
  235.     const overrideConsoleMethods = [
    
  236.       'error',
    
  237.       'group',
    
  238.       'groupCollapsed',
    
  239.       'info',
    
  240.       'log',
    
  241.       'trace',
    
  242.       'warn',
    
  243.     ];
    
  244. 
    
  245.     if (unpatchFn !== null) {
    
  246.       // Don't patch twice.
    
  247.       return;
    
  248.     }
    
  249. 
    
  250.     const originalConsoleMethods: {[string]: $FlowFixMe} = {};
    
  251. 
    
  252.     unpatchFn = () => {
    
  253.       for (const method in originalConsoleMethods) {
    
  254.         try {
    
  255.           targetConsole[method] = originalConsoleMethods[method];
    
  256.         } catch (error) {}
    
  257.       }
    
  258.     };
    
  259. 
    
  260.     overrideConsoleMethods.forEach(method => {
    
  261.       try {
    
  262.         const originalMethod = (originalConsoleMethods[method] = targetConsole[
    
  263.           method
    
  264.         ].__REACT_DEVTOOLS_STRICT_MODE_ORIGINAL_METHOD__
    
  265.           ? targetConsole[method].__REACT_DEVTOOLS_STRICT_MODE_ORIGINAL_METHOD__
    
  266.           : targetConsole[method]);
    
  267. 
    
  268.         const overrideMethod = (...args: $ReadOnlyArray<any>) => {
    
  269.           if (!hideConsoleLogsInStrictMode) {
    
  270.             // Dim the text color of the double logs if we're not
    
  271.             // hiding them.
    
  272.             let color;
    
  273.             switch (method) {
    
  274.               case 'warn':
    
  275.                 color =
    
  276.                   browserTheme === 'light'
    
  277.                     ? process.env.LIGHT_MODE_DIMMED_WARNING_COLOR
    
  278.                     : process.env.DARK_MODE_DIMMED_WARNING_COLOR;
    
  279.                 break;
    
  280.               case 'error':
    
  281.                 color =
    
  282.                   browserTheme === 'light'
    
  283.                     ? process.env.LIGHT_MODE_DIMMED_ERROR_COLOR
    
  284.                     : process.env.DARK_MODE_DIMMED_ERROR_COLOR;
    
  285.                 break;
    
  286.               case 'log':
    
  287.               default:
    
  288.                 color =
    
  289.                   browserTheme === 'light'
    
  290.                     ? process.env.LIGHT_MODE_DIMMED_LOG_COLOR
    
  291.                     : process.env.DARK_MODE_DIMMED_LOG_COLOR;
    
  292.                 break;
    
  293.             }
    
  294. 
    
  295.             if (color) {
    
  296.               originalMethod(...formatWithStyles(args, `color: ${color}`));
    
  297.             } else {
    
  298.               throw Error('Console color is not defined');
    
  299.             }
    
  300.           }
    
  301.         };
    
  302. 
    
  303.         overrideMethod.__REACT_DEVTOOLS_STRICT_MODE_ORIGINAL_METHOD__ =
    
  304.           originalMethod;
    
  305.         originalMethod.__REACT_DEVTOOLS_STRICT_MODE_OVERRIDE_METHOD__ =
    
  306.           overrideMethod;
    
  307. 
    
  308.         targetConsole[method] = overrideMethod;
    
  309.       } catch (error) {}
    
  310.     });
    
  311.   }
    
  312. 
    
  313.   // NOTE: KEEP IN SYNC with src/backend/console.js:unpatchForStrictMode
    
  314.   function unpatchConsoleForInitialRenderInStrictMode() {
    
  315.     if (unpatchFn !== null) {
    
  316.       unpatchFn();
    
  317.       unpatchFn = null;
    
  318.     }
    
  319.   }
    
  320. 
    
  321.   let uidCounter = 0;
    
  322. 
    
  323.   function inject(renderer: ReactRenderer): number {
    
  324.     const id = ++uidCounter;
    
  325.     renderers.set(id, renderer);
    
  326. 
    
  327.     const reactBuildType = hasDetectedBadDCE
    
  328.       ? 'deadcode'
    
  329.       : detectReactBuildType(renderer);
    
  330. 
    
  331.     // Patching the console enables DevTools to do a few useful things:
    
  332.     // * Append component stacks to warnings and error messages
    
  333.     // * Disabling or marking logs during a double render in Strict Mode
    
  334.     // * Disable logging during re-renders to inspect hooks (see inspectHooksOfFiber)
    
  335.     //
    
  336.     // Allow patching console early (during injection) to
    
  337.     // provide developers with components stacks even if they don't run DevTools.
    
  338.     if (target.hasOwnProperty('__REACT_DEVTOOLS_CONSOLE_FUNCTIONS__')) {
    
  339.       const {registerRendererWithConsole, patchConsoleUsingWindowValues} =
    
  340.         target.__REACT_DEVTOOLS_CONSOLE_FUNCTIONS__;
    
  341.       if (
    
  342.         typeof registerRendererWithConsole === 'function' &&
    
  343.         typeof patchConsoleUsingWindowValues === 'function'
    
  344.       ) {
    
  345.         registerRendererWithConsole(renderer);
    
  346.         patchConsoleUsingWindowValues();
    
  347.       }
    
  348.     }
    
  349. 
    
  350.     // If we have just reloaded to profile, we need to inject the renderer interface before the app loads.
    
  351.     // Otherwise the renderer won't yet exist and we can skip this step.
    
  352.     const attach = target.__REACT_DEVTOOLS_ATTACH__;
    
  353.     if (typeof attach === 'function') {
    
  354.       const rendererInterface = attach(hook, id, renderer, target);
    
  355.       hook.rendererInterfaces.set(id, rendererInterface);
    
  356.     }
    
  357. 
    
  358.     hook.emit('renderer', {
    
  359.       id,
    
  360.       renderer,
    
  361.       reactBuildType,
    
  362.     });
    
  363. 
    
  364.     return id;
    
  365.   }
    
  366. 
    
  367.   let hasDetectedBadDCE = false;
    
  368. 
    
  369.   function sub(event: string, fn: Handler) {
    
  370.     hook.on(event, fn);
    
  371.     return () => hook.off(event, fn);
    
  372.   }
    
  373. 
    
  374.   function on(event: string, fn: Handler) {
    
  375.     if (!listeners[event]) {
    
  376.       listeners[event] = [];
    
  377.     }
    
  378.     listeners[event].push(fn);
    
  379.   }
    
  380. 
    
  381.   function off(event: string, fn: Handler) {
    
  382.     if (!listeners[event]) {
    
  383.       return;
    
  384.     }
    
  385.     const index = listeners[event].indexOf(fn);
    
  386.     if (index !== -1) {
    
  387.       listeners[event].splice(index, 1);
    
  388.     }
    
  389.     if (!listeners[event].length) {
    
  390.       delete listeners[event];
    
  391.     }
    
  392.   }
    
  393. 
    
  394.   function emit(event: string, data: any) {
    
  395.     if (listeners[event]) {
    
  396.       listeners[event].map(fn => fn(data));
    
  397.     }
    
  398.   }
    
  399. 
    
  400.   function getFiberRoots(rendererID: RendererID) {
    
  401.     const roots = fiberRoots;
    
  402.     if (!roots[rendererID]) {
    
  403.       roots[rendererID] = new Set();
    
  404.     }
    
  405.     return roots[rendererID];
    
  406.   }
    
  407. 
    
  408.   function onCommitFiberUnmount(rendererID: RendererID, fiber: any) {
    
  409.     const rendererInterface = rendererInterfaces.get(rendererID);
    
  410.     if (rendererInterface != null) {
    
  411.       rendererInterface.handleCommitFiberUnmount(fiber);
    
  412.     }
    
  413.   }
    
  414. 
    
  415.   function onCommitFiberRoot(
    
  416.     rendererID: RendererID,
    
  417.     root: any,
    
  418.     priorityLevel: void | number,
    
  419.   ) {
    
  420.     const mountedRoots = hook.getFiberRoots(rendererID);
    
  421.     const current = root.current;
    
  422.     const isKnownRoot = mountedRoots.has(root);
    
  423.     const isUnmounting =
    
  424.       current.memoizedState == null || current.memoizedState.element == null;
    
  425. 
    
  426.     // Keep track of mounted roots so we can hydrate when DevTools connect.
    
  427.     if (!isKnownRoot && !isUnmounting) {
    
  428.       mountedRoots.add(root);
    
  429.     } else if (isKnownRoot && isUnmounting) {
    
  430.       mountedRoots.delete(root);
    
  431.     }
    
  432.     const rendererInterface = rendererInterfaces.get(rendererID);
    
  433.     if (rendererInterface != null) {
    
  434.       rendererInterface.handleCommitFiberRoot(root, priorityLevel);
    
  435.     }
    
  436.   }
    
  437. 
    
  438.   function onPostCommitFiberRoot(rendererID: RendererID, root: any) {
    
  439.     const rendererInterface = rendererInterfaces.get(rendererID);
    
  440.     if (rendererInterface != null) {
    
  441.       rendererInterface.handlePostCommitFiberRoot(root);
    
  442.     }
    
  443.   }
    
  444. 
    
  445.   function setStrictMode(rendererID: RendererID, isStrictMode: any) {
    
  446.     const rendererInterface = rendererInterfaces.get(rendererID);
    
  447.     if (rendererInterface != null) {
    
  448.       if (isStrictMode) {
    
  449.         rendererInterface.patchConsoleForStrictMode();
    
  450.       } else {
    
  451.         rendererInterface.unpatchConsoleForStrictMode();
    
  452.       }
    
  453.     } else {
    
  454.       // This should only happen during initial render in the extension before DevTools
    
  455.       // finishes its handshake with the injected renderer
    
  456.       if (isStrictMode) {
    
  457.         const hideConsoleLogsInStrictMode =
    
  458.           window.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ === true;
    
  459.         const browserTheme = window.__REACT_DEVTOOLS_BROWSER_THEME__;
    
  460. 
    
  461.         patchConsoleForInitialRenderInStrictMode({
    
  462.           hideConsoleLogsInStrictMode,
    
  463.           browserTheme,
    
  464.         });
    
  465.       } else {
    
  466.         unpatchConsoleForInitialRenderInStrictMode();
    
  467.       }
    
  468.     }
    
  469.   }
    
  470. 
    
  471.   type StackFrameString = string;
    
  472. 
    
  473.   const openModuleRangesStack: Array<StackFrameString> = [];
    
  474.   const moduleRanges: Array<[StackFrameString, StackFrameString]> = [];
    
  475. 
    
  476.   function getTopStackFrameString(error: Error): StackFrameString | null {
    
  477.     const frames = error.stack.split('\n');
    
  478.     const frame = frames.length > 1 ? frames[1] : null;
    
  479.     return frame;
    
  480.   }
    
  481. 
    
  482.   function getInternalModuleRanges(): Array<
    
  483.     [StackFrameString, StackFrameString],
    
  484.   > {
    
  485.     return moduleRanges;
    
  486.   }
    
  487. 
    
  488.   function registerInternalModuleStart(error: Error) {
    
  489.     const startStackFrame = getTopStackFrameString(error);
    
  490.     if (startStackFrame !== null) {
    
  491.       openModuleRangesStack.push(startStackFrame);
    
  492.     }
    
  493.   }
    
  494. 
    
  495.   function registerInternalModuleStop(error: Error) {
    
  496.     if (openModuleRangesStack.length > 0) {
    
  497.       const startStackFrame = openModuleRangesStack.pop();
    
  498.       const stopStackFrame = getTopStackFrameString(error);
    
  499.       if (stopStackFrame !== null) {
    
  500.         moduleRanges.push([startStackFrame, stopStackFrame]);
    
  501.       }
    
  502.     }
    
  503.   }
    
  504. 
    
  505.   // TODO: More meaningful names for "rendererInterfaces" and "renderers".
    
  506.   const fiberRoots: {[RendererID]: Set<mixed>} = {};
    
  507.   const rendererInterfaces = new Map<RendererID, RendererInterface>();
    
  508.   const listeners: {[string]: Array<Handler>} = {};
    
  509.   const renderers = new Map<RendererID, ReactRenderer>();
    
  510.   const backends = new Map<string, DevToolsBackend>();
    
  511. 
    
  512.   const hook: DevToolsHook = {
    
  513.     rendererInterfaces,
    
  514.     listeners,
    
  515. 
    
  516.     backends,
    
  517. 
    
  518.     // Fast Refresh for web relies on this.
    
  519.     renderers,
    
  520. 
    
  521.     emit,
    
  522.     getFiberRoots,
    
  523.     inject,
    
  524.     on,
    
  525.     off,
    
  526.     sub,
    
  527. 
    
  528.     // This is a legacy flag.
    
  529.     // React v16 checks the hook for this to ensure DevTools is new enough.
    
  530.     supportsFiber: true,
    
  531. 
    
  532.     // React calls these methods.
    
  533.     checkDCE,
    
  534.     onCommitFiberUnmount,
    
  535.     onCommitFiberRoot,
    
  536.     onPostCommitFiberRoot,
    
  537.     setStrictMode,
    
  538. 
    
  539.     // Schedule Profiler runtime helpers.
    
  540.     // These internal React modules to report their own boundaries
    
  541.     // which in turn enables the profiler to dim or filter internal frames.
    
  542.     getInternalModuleRanges,
    
  543.     registerInternalModuleStart,
    
  544.     registerInternalModuleStop,
    
  545.   };
    
  546. 
    
  547.   if (__TEST__) {
    
  548.     hook.dangerous_setTargetConsoleForTesting =
    
  549.       dangerous_setTargetConsoleForTesting;
    
  550.   }
    
  551. 
    
  552.   Object.defineProperty(
    
  553.     target,
    
  554.     '__REACT_DEVTOOLS_GLOBAL_HOOK__',
    
  555.     ({
    
  556.       // This property needs to be configurable for the test environment,
    
  557.       // else we won't be able to delete and recreate it between tests.
    
  558.       configurable: __DEV__,
    
  559.       enumerable: false,
    
  560.       get() {
    
  561.         return hook;
    
  562.       },
    
  563.     }: Object),
    
  564.   );
    
  565. 
    
  566.   return hook;
    
  567. }