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 ESLintTester = require('eslint').RuleTester;
    
  11. const ReactHooksESLintPlugin = require('eslint-plugin-react-hooks');
    
  12. const ReactHooksESLintRule = ReactHooksESLintPlugin.rules['rules-of-hooks'];
    
  13. 
    
  14. ESLintTester.setDefaultConfig({
    
  15.   parser: require.resolve('babel-eslint'),
    
  16.   parserOptions: {
    
  17.     ecmaVersion: 6,
    
  18.     sourceType: 'module',
    
  19.   },
    
  20. });
    
  21. 
    
  22. /**
    
  23.  * A string template tag that removes padding from the left side of multi-line strings
    
  24.  * @param {Array} strings array of code strings (only one expected)
    
  25.  */
    
  26. function normalizeIndent(strings) {
    
  27.   const codeLines = strings[0].split('\n');
    
  28.   const leftPadding = codeLines[1].match(/\s+/)[0];
    
  29.   return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
    
  30. }
    
  31. 
    
  32. // ***************************************************
    
  33. // For easier local testing, you can add to any case:
    
  34. // {
    
  35. //   skip: true,
    
  36. //   --or--
    
  37. //   only: true,
    
  38. //   ...
    
  39. // }
    
  40. // ***************************************************
    
  41. 
    
  42. const tests = {
    
  43.   valid: [
    
  44.     {
    
  45.       code: normalizeIndent`
    
  46.         // Valid because components can use hooks.
    
  47.         function ComponentWithHook() {
    
  48.           useHook();
    
  49.         }
    
  50.       `,
    
  51.     },
    
  52.     {
    
  53.       code: normalizeIndent`
    
  54.         // Valid because components can use hooks.
    
  55.         function createComponentWithHook() {
    
  56.           return function ComponentWithHook() {
    
  57.             useHook();
    
  58.           };
    
  59.         }
    
  60.       `,
    
  61.     },
    
  62.     {
    
  63.       code: normalizeIndent`
    
  64.         // Valid because hooks can use hooks.
    
  65.         function useHookWithHook() {
    
  66.           useHook();
    
  67.         }
    
  68.       `,
    
  69.     },
    
  70.     {
    
  71.       code: normalizeIndent`
    
  72.         // Valid because hooks can use hooks.
    
  73.         function createHook() {
    
  74.           return function useHookWithHook() {
    
  75.             useHook();
    
  76.           }
    
  77.         }
    
  78.       `,
    
  79.     },
    
  80.     {
    
  81.       code: normalizeIndent`
    
  82.         // Valid because components can call functions.
    
  83.         function ComponentWithNormalFunction() {
    
  84.           doSomething();
    
  85.         }
    
  86.       `,
    
  87.     },
    
  88.     {
    
  89.       code: normalizeIndent`
    
  90.         // Valid because functions can call functions.
    
  91.         function normalFunctionWithNormalFunction() {
    
  92.           doSomething();
    
  93.         }
    
  94.       `,
    
  95.     },
    
  96.     {
    
  97.       code: normalizeIndent`
    
  98.         // Valid because functions can call functions.
    
  99.         function normalFunctionWithConditionalFunction() {
    
  100.           if (cond) {
    
  101.             doSomething();
    
  102.           }
    
  103.         }
    
  104.       `,
    
  105.     },
    
  106.     {
    
  107.       code: normalizeIndent`
    
  108.         // Valid because functions can call functions.
    
  109.         function functionThatStartsWithUseButIsntAHook() {
    
  110.           if (cond) {
    
  111.             userFetch();
    
  112.           }
    
  113.         }
    
  114.       `,
    
  115.     },
    
  116.     {
    
  117.       code: normalizeIndent`
    
  118.         // Valid although unconditional return doesn't make sense and would fail other rules.
    
  119.         // We could make it invalid but it doesn't matter.
    
  120.         function useUnreachable() {
    
  121.           return;
    
  122.           useHook();
    
  123.         }
    
  124.       `,
    
  125.     },
    
  126.     {
    
  127.       code: normalizeIndent`
    
  128.         // Valid because hooks can call hooks.
    
  129.         function useHook() { useState(); }
    
  130.         const whatever = function useHook() { useState(); };
    
  131.         const useHook1 = () => { useState(); };
    
  132.         let useHook2 = () => useState();
    
  133.         useHook2 = () => { useState(); };
    
  134.         ({useHook: () => { useState(); }});
    
  135.         ({useHook() { useState(); }});
    
  136.         const {useHook3 = () => { useState(); }} = {};
    
  137.         ({useHook = () => { useState(); }} = {});
    
  138.         Namespace.useHook = () => { useState(); };
    
  139.       `,
    
  140.     },
    
  141.     {
    
  142.       code: normalizeIndent`
    
  143.         // Valid because hooks can call hooks.
    
  144.         function useHook() {
    
  145.           useHook1();
    
  146.           useHook2();
    
  147.         }
    
  148.       `,
    
  149.     },
    
  150.     {
    
  151.       code: normalizeIndent`
    
  152.         // Valid because hooks can call hooks.
    
  153.         function createHook() {
    
  154.           return function useHook() {
    
  155.             useHook1();
    
  156.             useHook2();
    
  157.           };
    
  158.         }
    
  159.       `,
    
  160.     },
    
  161.     {
    
  162.       code: normalizeIndent`
    
  163.         // Valid because hooks can call hooks.
    
  164.         function useHook() {
    
  165.           useState() && a;
    
  166.         }
    
  167.       `,
    
  168.     },
    
  169.     {
    
  170.       code: normalizeIndent`
    
  171.         // Valid because hooks can call hooks.
    
  172.         function useHook() {
    
  173.           return useHook1() + useHook2();
    
  174.         }
    
  175.       `,
    
  176.     },
    
  177.     {
    
  178.       code: normalizeIndent`
    
  179.         // Valid because hooks can call hooks.
    
  180.         function useHook() {
    
  181.           return useHook1(useHook2());
    
  182.         }
    
  183.       `,
    
  184.     },
    
  185.     {
    
  186.       code: normalizeIndent`
    
  187.         // Valid because hooks can be used in anonymous arrow-function arguments
    
  188.         // to forwardRef.
    
  189.         const FancyButton = React.forwardRef((props, ref) => {
    
  190.           useHook();
    
  191.           return <button {...props} ref={ref} />
    
  192.         });
    
  193.       `,
    
  194.     },
    
  195.     {
    
  196.       code: normalizeIndent`
    
  197.         // Valid because hooks can be used in anonymous function arguments to
    
  198.         // forwardRef.
    
  199.         const FancyButton = React.forwardRef(function (props, ref) {
    
  200.           useHook();
    
  201.           return <button {...props} ref={ref} />
    
  202.         });
    
  203.       `,
    
  204.     },
    
  205.     {
    
  206.       code: normalizeIndent`
    
  207.         // Valid because hooks can be used in anonymous function arguments to
    
  208.         // forwardRef.
    
  209.         const FancyButton = forwardRef(function (props, ref) {
    
  210.           useHook();
    
  211.           return <button {...props} ref={ref} />
    
  212.         });
    
  213.       `,
    
  214.     },
    
  215.     {
    
  216.       code: normalizeIndent`
    
  217.         // Valid because hooks can be used in anonymous function arguments to
    
  218.         // React.memo.
    
  219.         const MemoizedFunction = React.memo(props => {
    
  220.           useHook();
    
  221.           return <button {...props} />
    
  222.         });
    
  223.       `,
    
  224.     },
    
  225.     {
    
  226.       code: normalizeIndent`
    
  227.         // Valid because hooks can be used in anonymous function arguments to
    
  228.         // memo.
    
  229.         const MemoizedFunction = memo(function (props) {
    
  230.           useHook();
    
  231.           return <button {...props} />
    
  232.         });
    
  233.       `,
    
  234.     },
    
  235.     {
    
  236.       code: normalizeIndent`
    
  237.         // Valid because classes can call functions.
    
  238.         // We don't consider these to be hooks.
    
  239.         class C {
    
  240.           m() {
    
  241.             this.useHook();
    
  242.             super.useHook();
    
  243.           }
    
  244.         }
    
  245.       `,
    
  246.     },
    
  247.     {
    
  248.       code: normalizeIndent`
    
  249.         // Valid -- this is a regression test.
    
  250.         jest.useFakeTimers();
    
  251.         beforeEach(() => {
    
  252.           jest.useRealTimers();
    
  253.         })
    
  254.       `,
    
  255.     },
    
  256.     {
    
  257.       code: normalizeIndent`
    
  258.         // Valid because they're not matching use[A-Z].
    
  259.         fooState();
    
  260.         _use();
    
  261.         _useState();
    
  262.         use_hook();
    
  263.         // also valid because it's not matching the PascalCase namespace
    
  264.         jest.useFakeTimer()
    
  265.       `,
    
  266.     },
    
  267.     {
    
  268.       code: normalizeIndent`
    
  269.         // Regression test for some internal code.
    
  270.         // This shows how the "callback rule" is more relaxed,
    
  271.         // and doesn't kick in unless we're confident we're in
    
  272.         // a component or a hook.
    
  273.         function makeListener(instance) {
    
  274.           each(pixelsWithInferredEvents, pixel => {
    
  275.             if (useExtendedSelector(pixel.id) && extendedButton) {
    
  276.               foo();
    
  277.             }
    
  278.           });
    
  279.         }
    
  280.       `,
    
  281.     },
    
  282.     {
    
  283.       code: normalizeIndent`
    
  284.         // This is valid because "use"-prefixed functions called in
    
  285.         // unnamed function arguments are not assumed to be hooks.
    
  286.         React.unknownFunction((foo, bar) => {
    
  287.           if (foo) {
    
  288.             useNotAHook(bar)
    
  289.           }
    
  290.         });
    
  291.       `,
    
  292.     },
    
  293.     {
    
  294.       code: normalizeIndent`
    
  295.         // This is valid because "use"-prefixed functions called in
    
  296.         // unnamed function arguments are not assumed to be hooks.
    
  297.         unknownFunction(function(foo, bar) {
    
  298.           if (foo) {
    
  299.             useNotAHook(bar)
    
  300.           }
    
  301.         });
    
  302.       `,
    
  303.     },
    
  304.     {
    
  305.       code: normalizeIndent`
    
  306.         // Regression test for incorrectly flagged valid code.
    
  307.         function RegressionTest() {
    
  308.           const foo = cond ? a : b;
    
  309.           useState();
    
  310.         }
    
  311.       `,
    
  312.     },
    
  313.     {
    
  314.       code: normalizeIndent`
    
  315.         // Valid because exceptions abort rendering
    
  316.         function RegressionTest() {
    
  317.           if (page == null) {
    
  318.             throw new Error('oh no!');
    
  319.           }
    
  320.           useState();
    
  321.         }
    
  322.       `,
    
  323.     },
    
  324.     {
    
  325.       code: normalizeIndent`
    
  326.         // Valid because the loop doesn't change the order of hooks calls.
    
  327.         function RegressionTest() {
    
  328.           const res = [];
    
  329.           const additionalCond = true;
    
  330.           for (let i = 0; i !== 10 && additionalCond; ++i ) {
    
  331.             res.push(i);
    
  332.           }
    
  333.           React.useLayoutEffect(() => {});
    
  334.         }
    
  335.       `,
    
  336.     },
    
  337.     {
    
  338.       code: normalizeIndent`
    
  339.         // Is valid but hard to compute by brute-forcing
    
  340.         function MyComponent() {
    
  341.           // 40 conditions
    
  342.           if (c) {} else {}
    
  343.           if (c) {} else {}
    
  344.           if (c) {} else {}
    
  345.           if (c) {} else {}
    
  346.           if (c) {} else {}
    
  347.           if (c) {} else {}
    
  348.           if (c) {} else {}
    
  349.           if (c) {} else {}
    
  350.           if (c) {} else {}
    
  351.           if (c) {} else {}
    
  352.           if (c) {} else {}
    
  353.           if (c) {} else {}
    
  354.           if (c) {} else {}
    
  355.           if (c) {} else {}
    
  356.           if (c) {} else {}
    
  357.           if (c) {} else {}
    
  358.           if (c) {} else {}
    
  359.           if (c) {} else {}
    
  360.           if (c) {} else {}
    
  361.           if (c) {} else {}
    
  362.           if (c) {} else {}
    
  363.           if (c) {} else {}
    
  364.           if (c) {} else {}
    
  365.           if (c) {} else {}
    
  366.           if (c) {} else {}
    
  367.           if (c) {} else {}
    
  368.           if (c) {} else {}
    
  369.           if (c) {} else {}
    
  370.           if (c) {} else {}
    
  371.           if (c) {} else {}
    
  372.           if (c) {} else {}
    
  373.           if (c) {} else {}
    
  374.           if (c) {} else {}
    
  375.           if (c) {} else {}
    
  376.           if (c) {} else {}
    
  377.           if (c) {} else {}
    
  378.           if (c) {} else {}
    
  379.           if (c) {} else {}
    
  380.           if (c) {} else {}
    
  381.           if (c) {} else {}
    
  382. 
    
  383.           // 10 hooks
    
  384.           useHook();
    
  385.           useHook();
    
  386.           useHook();
    
  387.           useHook();
    
  388.           useHook();
    
  389.           useHook();
    
  390.           useHook();
    
  391.           useHook();
    
  392.           useHook();
    
  393.           useHook();
    
  394.         }
    
  395.       `,
    
  396.     },
    
  397.     {
    
  398.       code: normalizeIndent`
    
  399.         // Valid because the neither the conditions before or after the hook affect the hook call
    
  400.         // Failed prior to implementing BigInt because pathsFromStartToEnd and allPathsFromStartToEnd were too big and had rounding errors
    
  401.         const useSomeHook = () => {};
    
  402. 
    
  403.         const SomeName = () => {
    
  404.           const filler = FILLER ?? FILLER ?? FILLER;
    
  405.           const filler2 = FILLER ?? FILLER ?? FILLER;
    
  406.           const filler3 = FILLER ?? FILLER ?? FILLER;
    
  407.           const filler4 = FILLER ?? FILLER ?? FILLER;
    
  408.           const filler5 = FILLER ?? FILLER ?? FILLER;
    
  409.           const filler6 = FILLER ?? FILLER ?? FILLER;
    
  410.           const filler7 = FILLER ?? FILLER ?? FILLER;
    
  411.           const filler8 = FILLER ?? FILLER ?? FILLER;
    
  412. 
    
  413.           useSomeHook();
    
  414. 
    
  415.           if (anyConditionCanEvenBeFalse) {
    
  416.             return null;
    
  417.           }
    
  418. 
    
  419.           return (
    
  420.             <React.Fragment>
    
  421.               {FILLER ? FILLER : FILLER}
    
  422.               {FILLER ? FILLER : FILLER}
    
  423.               {FILLER ? FILLER : FILLER}
    
  424.               {FILLER ? FILLER : FILLER}
    
  425.               {FILLER ? FILLER : FILLER}
    
  426.               {FILLER ? FILLER : FILLER}
    
  427.               {FILLER ? FILLER : FILLER}
    
  428.               {FILLER ? FILLER : FILLER}
    
  429.               {FILLER ? FILLER : FILLER}
    
  430.               {FILLER ? FILLER : FILLER}
    
  431.               {FILLER ? FILLER : FILLER}
    
  432.               {FILLER ? FILLER : FILLER}
    
  433.               {FILLER ? FILLER : FILLER}
    
  434.               {FILLER ? FILLER : FILLER}
    
  435.               {FILLER ? FILLER : FILLER}
    
  436.               {FILLER ? FILLER : FILLER}
    
  437.               {FILLER ? FILLER : FILLER}
    
  438.               {FILLER ? FILLER : FILLER}
    
  439.               {FILLER ? FILLER : FILLER}
    
  440.               {FILLER ? FILLER : FILLER}
    
  441.               {FILLER ? FILLER : FILLER}
    
  442.               {FILLER ? FILLER : FILLER}
    
  443.               {FILLER ? FILLER : FILLER}
    
  444.               {FILLER ? FILLER : FILLER}
    
  445.               {FILLER ? FILLER : FILLER}
    
  446.               {FILLER ? FILLER : FILLER}
    
  447.               {FILLER ? FILLER : FILLER}
    
  448.               {FILLER ? FILLER : FILLER}
    
  449.               {FILLER ? FILLER : FILLER}
    
  450.               {FILLER ? FILLER : FILLER}
    
  451.               {FILLER ? FILLER : FILLER}
    
  452.               {FILLER ? FILLER : FILLER}
    
  453.               {FILLER ? FILLER : FILLER}
    
  454.               {FILLER ? FILLER : FILLER}
    
  455.               {FILLER ? FILLER : FILLER}
    
  456.               {FILLER ? FILLER : FILLER}
    
  457.               {FILLER ? FILLER : FILLER}
    
  458.               {FILLER ? FILLER : FILLER}
    
  459.               {FILLER ? FILLER : FILLER}
    
  460.               {FILLER ? FILLER : FILLER}
    
  461.               {FILLER ? FILLER : FILLER}
    
  462.               {FILLER ? FILLER : FILLER}
    
  463.             </React.Fragment>
    
  464.           );
    
  465.         };
    
  466.       `,
    
  467.     },
    
  468.     {
    
  469.       code: normalizeIndent`
    
  470.         // Valid because the neither the condition nor the loop affect the hook call.
    
  471.         function App(props) {
    
  472.           const someObject = {propA: true};
    
  473.           for (const propName in someObject) {
    
  474.             if (propName === true) {
    
  475.             } else {
    
  476.             }
    
  477.           }
    
  478.           const [myState, setMyState] = useState(null);
    
  479.         }
    
  480.       `,
    
  481.     },
    
  482.   ],
    
  483.   invalid: [
    
  484.     {
    
  485.       code: normalizeIndent`
    
  486.         // Invalid because it's dangerous and might not warn otherwise.
    
  487.         // This *must* be invalid.
    
  488.         function ComponentWithConditionalHook() {
    
  489.           if (cond) {
    
  490.             useConditionalHook();
    
  491.           }
    
  492.         }
    
  493.       `,
    
  494.       errors: [conditionalError('useConditionalHook')],
    
  495.     },
    
  496.     {
    
  497.       code: normalizeIndent`
    
  498.         Hook.useState();
    
  499.         Hook._useState();
    
  500.         Hook.use42();
    
  501.         Hook.useHook();
    
  502.         Hook.use_hook();
    
  503.       `,
    
  504.       errors: [
    
  505.         topLevelError('Hook.useState'),
    
  506.         topLevelError('Hook.use42'),
    
  507.         topLevelError('Hook.useHook'),
    
  508.       ],
    
  509.     },
    
  510.     {
    
  511.       code: normalizeIndent`
    
  512.         class C {
    
  513.           m() {
    
  514.             This.useHook();
    
  515.             Super.useHook();
    
  516.           }
    
  517.         }
    
  518.       `,
    
  519.       errors: [classError('This.useHook'), classError('Super.useHook')],
    
  520.     },
    
  521.     {
    
  522.       code: normalizeIndent`
    
  523.         // This is a false positive (it's valid) that unfortunately
    
  524.         // we cannot avoid. Prefer to rename it to not start with "use"
    
  525.         class Foo extends Component {
    
  526.           render() {
    
  527.             if (cond) {
    
  528.               FooStore.useFeatureFlag();
    
  529.             }
    
  530.           }
    
  531.         }
    
  532.       `,
    
  533.       errors: [classError('FooStore.useFeatureFlag')],
    
  534.     },
    
  535.     {
    
  536.       code: normalizeIndent`
    
  537.         // Invalid because it's dangerous and might not warn otherwise.
    
  538.         // This *must* be invalid.
    
  539.         function ComponentWithConditionalHook() {
    
  540.           if (cond) {
    
  541.             Namespace.useConditionalHook();
    
  542.           }
    
  543.         }
    
  544.       `,
    
  545.       errors: [conditionalError('Namespace.useConditionalHook')],
    
  546.     },
    
  547.     {
    
  548.       code: normalizeIndent`
    
  549.         // Invalid because it's dangerous and might not warn otherwise.
    
  550.         // This *must* be invalid.
    
  551.         function createComponent() {
    
  552.           return function ComponentWithConditionalHook() {
    
  553.             if (cond) {
    
  554.               useConditionalHook();
    
  555.             }
    
  556.           }
    
  557.         }
    
  558.       `,
    
  559.       errors: [conditionalError('useConditionalHook')],
    
  560.     },
    
  561.     {
    
  562.       code: normalizeIndent`
    
  563.         // Invalid because it's dangerous and might not warn otherwise.
    
  564.         // This *must* be invalid.
    
  565.         function useHookWithConditionalHook() {
    
  566.           if (cond) {
    
  567.             useConditionalHook();
    
  568.           }
    
  569.         }
    
  570.       `,
    
  571.       errors: [conditionalError('useConditionalHook')],
    
  572.     },
    
  573.     {
    
  574.       code: normalizeIndent`
    
  575.         // Invalid because it's dangerous and might not warn otherwise.
    
  576.         // This *must* be invalid.
    
  577.         function createHook() {
    
  578.           return function useHookWithConditionalHook() {
    
  579.             if (cond) {
    
  580.               useConditionalHook();
    
  581.             }
    
  582.           }
    
  583.         }
    
  584.       `,
    
  585.       errors: [conditionalError('useConditionalHook')],
    
  586.     },
    
  587.     {
    
  588.       code: normalizeIndent`
    
  589.         // Invalid because it's dangerous and might not warn otherwise.
    
  590.         // This *must* be invalid.
    
  591.         function ComponentWithTernaryHook() {
    
  592.           cond ? useTernaryHook() : null;
    
  593.         }
    
  594.       `,
    
  595.       errors: [conditionalError('useTernaryHook')],
    
  596.     },
    
  597.     {
    
  598.       code: normalizeIndent`
    
  599.         // Invalid because it's a common misunderstanding.
    
  600.         // We *could* make it valid but the runtime error could be confusing.
    
  601.         function ComponentWithHookInsideCallback() {
    
  602.           useEffect(() => {
    
  603.             useHookInsideCallback();
    
  604.           });
    
  605.         }
    
  606.       `,
    
  607.       errors: [genericError('useHookInsideCallback')],
    
  608.     },
    
  609.     {
    
  610.       code: normalizeIndent`
    
  611.         // Invalid because it's a common misunderstanding.
    
  612.         // We *could* make it valid but the runtime error could be confusing.
    
  613.         function createComponent() {
    
  614.           return function ComponentWithHookInsideCallback() {
    
  615.             useEffect(() => {
    
  616.               useHookInsideCallback();
    
  617.             });
    
  618.           }
    
  619.         }
    
  620.       `,
    
  621.       errors: [genericError('useHookInsideCallback')],
    
  622.     },
    
  623.     {
    
  624.       code: normalizeIndent`
    
  625.         // Invalid because it's a common misunderstanding.
    
  626.         // We *could* make it valid but the runtime error could be confusing.
    
  627.         const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
    
  628.           useEffect(() => {
    
  629.             useHookInsideCallback();
    
  630.           });
    
  631.           return <button {...props} ref={ref} />
    
  632.         });
    
  633.       `,
    
  634.       errors: [genericError('useHookInsideCallback')],
    
  635.     },
    
  636.     {
    
  637.       code: normalizeIndent`
    
  638.         // Invalid because it's a common misunderstanding.
    
  639.         // We *could* make it valid but the runtime error could be confusing.
    
  640.         const ComponentWithHookInsideCallback = React.memo(props => {
    
  641.           useEffect(() => {
    
  642.             useHookInsideCallback();
    
  643.           });
    
  644.           return <button {...props} />
    
  645.         });
    
  646.       `,
    
  647.       errors: [genericError('useHookInsideCallback')],
    
  648.     },
    
  649.     {
    
  650.       code: normalizeIndent`
    
  651.         // Invalid because it's a common misunderstanding.
    
  652.         // We *could* make it valid but the runtime error could be confusing.
    
  653.         function ComponentWithHookInsideCallback() {
    
  654.           function handleClick() {
    
  655.             useState();
    
  656.           }
    
  657.         }
    
  658.       `,
    
  659.       errors: [functionError('useState', 'handleClick')],
    
  660.     },
    
  661.     {
    
  662.       code: normalizeIndent`
    
  663.         // Invalid because it's a common misunderstanding.
    
  664.         // We *could* make it valid but the runtime error could be confusing.
    
  665.         function createComponent() {
    
  666.           return function ComponentWithHookInsideCallback() {
    
  667.             function handleClick() {
    
  668.               useState();
    
  669.             }
    
  670.           }
    
  671.         }
    
  672.       `,
    
  673.       errors: [functionError('useState', 'handleClick')],
    
  674.     },
    
  675.     {
    
  676.       code: normalizeIndent`
    
  677.         // Invalid because it's dangerous and might not warn otherwise.
    
  678.         // This *must* be invalid.
    
  679.         function ComponentWithHookInsideLoop() {
    
  680.           while (cond) {
    
  681.             useHookInsideLoop();
    
  682.           }
    
  683.         }
    
  684.       `,
    
  685.       errors: [loopError('useHookInsideLoop')],
    
  686.     },
    
  687.     {
    
  688.       code: normalizeIndent`
    
  689.         // Invalid because it's dangerous and might not warn otherwise.
    
  690.         // This *must* be invalid.
    
  691.         function renderItem() {
    
  692.           useState();
    
  693.         }
    
  694. 
    
  695.         function List(props) {
    
  696.           return props.items.map(renderItem);
    
  697.         }
    
  698.       `,
    
  699.       errors: [functionError('useState', 'renderItem')],
    
  700.     },
    
  701.     {
    
  702.       code: normalizeIndent`
    
  703.         // Currently invalid because it violates the convention and removes the "taint"
    
  704.         // from a hook. We *could* make it valid to avoid some false positives but let's
    
  705.         // ensure that we don't break the "renderItem" and "normalFunctionWithConditionalHook"
    
  706.         // cases which must remain invalid.
    
  707.         function normalFunctionWithHook() {
    
  708.           useHookInsideNormalFunction();
    
  709.         }
    
  710.       `,
    
  711.       errors: [
    
  712.         functionError('useHookInsideNormalFunction', 'normalFunctionWithHook'),
    
  713.       ],
    
  714.     },
    
  715.     {
    
  716.       code: normalizeIndent`
    
  717.         // These are neither functions nor hooks.
    
  718.         function _normalFunctionWithHook() {
    
  719.           useHookInsideNormalFunction();
    
  720.         }
    
  721.         function _useNotAHook() {
    
  722.           useHookInsideNormalFunction();
    
  723.         }
    
  724.       `,
    
  725.       errors: [
    
  726.         functionError('useHookInsideNormalFunction', '_normalFunctionWithHook'),
    
  727.         functionError('useHookInsideNormalFunction', '_useNotAHook'),
    
  728.       ],
    
  729.     },
    
  730.     {
    
  731.       code: normalizeIndent`
    
  732.         // Invalid because it's dangerous and might not warn otherwise.
    
  733.         // This *must* be invalid.
    
  734.         function normalFunctionWithConditionalHook() {
    
  735.           if (cond) {
    
  736.             useHookInsideNormalFunction();
    
  737.           }
    
  738.         }
    
  739.       `,
    
  740.       errors: [
    
  741.         functionError(
    
  742.           'useHookInsideNormalFunction',
    
  743.           'normalFunctionWithConditionalHook'
    
  744.         ),
    
  745.       ],
    
  746.     },
    
  747.     {
    
  748.       code: normalizeIndent`
    
  749.         // Invalid because it's dangerous and might not warn otherwise.
    
  750.         // This *must* be invalid.
    
  751.         function useHookInLoops() {
    
  752.           while (a) {
    
  753.             useHook1();
    
  754.             if (b) return;
    
  755.             useHook2();
    
  756.           }
    
  757.           while (c) {
    
  758.             useHook3();
    
  759.             if (d) return;
    
  760.             useHook4();
    
  761.           }
    
  762.         }
    
  763.       `,
    
  764.       errors: [
    
  765.         loopError('useHook1'),
    
  766.         loopError('useHook2'),
    
  767.         loopError('useHook3'),
    
  768.         loopError('useHook4'),
    
  769.       ],
    
  770.     },
    
  771.     {
    
  772.       code: normalizeIndent`
    
  773.         // Invalid because it's dangerous and might not warn otherwise.
    
  774.         // This *must* be invalid.
    
  775.         function useHookInLoops() {
    
  776.           while (a) {
    
  777.             useHook1();
    
  778.             if (b) continue;
    
  779.             useHook2();
    
  780.           }
    
  781.         }
    
  782.       `,
    
  783.       errors: [loopError('useHook1'), loopError('useHook2', true)],
    
  784.     },
    
  785.     {
    
  786.       code: normalizeIndent`
    
  787.         // Invalid because it's dangerous and might not warn otherwise.
    
  788.         // This *must* be invalid.
    
  789.         function useLabeledBlock() {
    
  790.           label: {
    
  791.             if (a) break label;
    
  792.             useHook();
    
  793.           }
    
  794.         }
    
  795.       `,
    
  796.       errors: [conditionalError('useHook')],
    
  797.     },
    
  798.     {
    
  799.       code: normalizeIndent`
    
  800.         // Currently invalid.
    
  801.         // These are variations capturing the current heuristic--
    
  802.         // we only allow hooks in PascalCase or useFoo functions.
    
  803.         // We *could* make some of these valid. But before doing it,
    
  804.         // consider specific cases documented above that contain reasoning.
    
  805.         function a() { useState(); }
    
  806.         const whatever = function b() { useState(); };
    
  807.         const c = () => { useState(); };
    
  808.         let d = () => useState();
    
  809.         e = () => { useState(); };
    
  810.         ({f: () => { useState(); }});
    
  811.         ({g() { useState(); }});
    
  812.         const {j = () => { useState(); }} = {};
    
  813.         ({k = () => { useState(); }} = {});
    
  814.       `,
    
  815.       errors: [
    
  816.         functionError('useState', 'a'),
    
  817.         functionError('useState', 'b'),
    
  818.         functionError('useState', 'c'),
    
  819.         functionError('useState', 'd'),
    
  820.         functionError('useState', 'e'),
    
  821.         functionError('useState', 'f'),
    
  822.         functionError('useState', 'g'),
    
  823.         functionError('useState', 'j'),
    
  824.         functionError('useState', 'k'),
    
  825.       ],
    
  826.     },
    
  827.     {
    
  828.       code: normalizeIndent`
    
  829.         // Invalid because it's dangerous and might not warn otherwise.
    
  830.         // This *must* be invalid.
    
  831.         function useHook() {
    
  832.           if (a) return;
    
  833.           useState();
    
  834.         }
    
  835.       `,
    
  836.       errors: [conditionalError('useState', true)],
    
  837.     },
    
  838.     {
    
  839.       code: normalizeIndent`
    
  840.         // Invalid because it's dangerous and might not warn otherwise.
    
  841.         // This *must* be invalid.
    
  842.         function useHook() {
    
  843.           if (a) return;
    
  844.           if (b) {
    
  845.             console.log('true');
    
  846.           } else {
    
  847.             console.log('false');
    
  848.           }
    
  849.           useState();
    
  850.         }
    
  851.       `,
    
  852.       errors: [conditionalError('useState', true)],
    
  853.     },
    
  854.     {
    
  855.       code: normalizeIndent`
    
  856.         // Invalid because it's dangerous and might not warn otherwise.
    
  857.         // This *must* be invalid.
    
  858.         function useHook() {
    
  859.           if (b) {
    
  860.             console.log('true');
    
  861.           } else {
    
  862.             console.log('false');
    
  863.           }
    
  864.           if (a) return;
    
  865.           useState();
    
  866.         }
    
  867.       `,
    
  868.       errors: [conditionalError('useState', true)],
    
  869.     },
    
  870.     {
    
  871.       code: normalizeIndent`
    
  872.         // Invalid because it's dangerous and might not warn otherwise.
    
  873.         // This *must* be invalid.
    
  874.         function useHook() {
    
  875.           a && useHook1();
    
  876.           b && useHook2();
    
  877.         }
    
  878.       `,
    
  879.       errors: [conditionalError('useHook1'), conditionalError('useHook2')],
    
  880.     },
    
  881.     {
    
  882.       code: normalizeIndent`
    
  883.         // Invalid because it's dangerous and might not warn otherwise.
    
  884.         // This *must* be invalid.
    
  885.         function useHook() {
    
  886.           try {
    
  887.             f();
    
  888.             useState();
    
  889.           } catch {}
    
  890.         }
    
  891.       `,
    
  892.       errors: [
    
  893.         // NOTE: This is an error since `f()` could possibly throw.
    
  894.         conditionalError('useState'),
    
  895.       ],
    
  896.     },
    
  897.     {
    
  898.       code: normalizeIndent`
    
  899.         // Invalid because it's dangerous and might not warn otherwise.
    
  900.         // This *must* be invalid.
    
  901.         function useHook({ bar }) {
    
  902.           let foo1 = bar && useState();
    
  903.           let foo2 = bar || useState();
    
  904.           let foo3 = bar ?? useState();
    
  905.         }
    
  906.       `,
    
  907.       errors: [
    
  908.         conditionalError('useState'),
    
  909.         conditionalError('useState'),
    
  910.         conditionalError('useState'),
    
  911.       ],
    
  912.     },
    
  913.     {
    
  914.       code: normalizeIndent`
    
  915.         // Invalid because it's dangerous and might not warn otherwise.
    
  916.         // This *must* be invalid.
    
  917.         const FancyButton = React.forwardRef((props, ref) => {
    
  918.           if (props.fancy) {
    
  919.             useCustomHook();
    
  920.           }
    
  921.           return <button ref={ref}>{props.children}</button>;
    
  922.         });
    
  923.       `,
    
  924.       errors: [conditionalError('useCustomHook')],
    
  925.     },
    
  926.     {
    
  927.       code: normalizeIndent`
    
  928.         // Invalid because it's dangerous and might not warn otherwise.
    
  929.         // This *must* be invalid.
    
  930.         const FancyButton = forwardRef(function(props, ref) {
    
  931.           if (props.fancy) {
    
  932.             useCustomHook();
    
  933.           }
    
  934.           return <button ref={ref}>{props.children}</button>;
    
  935.         });
    
  936.       `,
    
  937.       errors: [conditionalError('useCustomHook')],
    
  938.     },
    
  939.     {
    
  940.       code: normalizeIndent`
    
  941.         // Invalid because it's dangerous and might not warn otherwise.
    
  942.         // This *must* be invalid.
    
  943.         const MemoizedButton = memo(function(props) {
    
  944.           if (props.fancy) {
    
  945.             useCustomHook();
    
  946.           }
    
  947.           return <button>{props.children}</button>;
    
  948.         });
    
  949.       `,
    
  950.       errors: [conditionalError('useCustomHook')],
    
  951.     },
    
  952.     {
    
  953.       code: normalizeIndent`
    
  954.         // This is invalid because "use"-prefixed functions used in named
    
  955.         // functions are assumed to be hooks.
    
  956.         React.unknownFunction(function notAComponent(foo, bar) {
    
  957.           useProbablyAHook(bar)
    
  958.         });
    
  959.       `,
    
  960.       errors: [functionError('useProbablyAHook', 'notAComponent')],
    
  961.     },
    
  962.     {
    
  963.       code: normalizeIndent`
    
  964.         // Invalid because it's dangerous.
    
  965.         // Normally, this would crash, but not if you use inline requires.
    
  966.         // This *must* be invalid.
    
  967.         // It's expected to have some false positives, but arguably
    
  968.         // they are confusing anyway due to the use*() convention
    
  969.         // already being associated with Hooks.
    
  970.         useState();
    
  971.         if (foo) {
    
  972.           const foo = React.useCallback(() => {});
    
  973.         }
    
  974.         useCustomHook();
    
  975.       `,
    
  976.       errors: [
    
  977.         topLevelError('useState'),
    
  978.         topLevelError('React.useCallback'),
    
  979.         topLevelError('useCustomHook'),
    
  980.       ],
    
  981.     },
    
  982.     {
    
  983.       code: normalizeIndent`
    
  984.         // Technically this is a false positive.
    
  985.         // We *could* make it valid (and it used to be).
    
  986.         //
    
  987.         // However, top-level Hook-like calls can be very dangerous
    
  988.         // in environments with inline requires because they can mask
    
  989.         // the runtime error by accident.
    
  990.         // So we prefer to disallow it despite the false positive.
    
  991. 
    
  992.         const {createHistory, useBasename} = require('history-2.1.2');
    
  993.         const browserHistory = useBasename(createHistory)({
    
  994.           basename: '/',
    
  995.         });
    
  996.       `,
    
  997.       errors: [topLevelError('useBasename')],
    
  998.     },
    
  999.     {
    
  1000.       code: normalizeIndent`
    
  1001.         class ClassComponentWithFeatureFlag extends React.Component {
    
  1002.           render() {
    
  1003.             if (foo) {
    
  1004.               useFeatureFlag();
    
  1005.             }
    
  1006.           }
    
  1007.         }
    
  1008.       `,
    
  1009.       errors: [classError('useFeatureFlag')],
    
  1010.     },
    
  1011.     {
    
  1012.       code: normalizeIndent`
    
  1013.         class ClassComponentWithHook extends React.Component {
    
  1014.           render() {
    
  1015.             React.useState();
    
  1016.           }
    
  1017.         }
    
  1018.       `,
    
  1019.       errors: [classError('React.useState')],
    
  1020.     },
    
  1021.     {
    
  1022.       code: normalizeIndent`
    
  1023.         (class {useHook = () => { useState(); }});
    
  1024.       `,
    
  1025.       errors: [classError('useState')],
    
  1026.     },
    
  1027.     {
    
  1028.       code: normalizeIndent`
    
  1029.         (class {useHook() { useState(); }});
    
  1030.       `,
    
  1031.       errors: [classError('useState')],
    
  1032.     },
    
  1033.     {
    
  1034.       code: normalizeIndent`
    
  1035.         (class {h = () => { useState(); }});
    
  1036.       `,
    
  1037.       errors: [classError('useState')],
    
  1038.     },
    
  1039.     {
    
  1040.       code: normalizeIndent`
    
  1041.         (class {i() { useState(); }});
    
  1042.       `,
    
  1043.       errors: [classError('useState')],
    
  1044.     },
    
  1045.     {
    
  1046.       code: normalizeIndent`
    
  1047.         async function AsyncComponent() {
    
  1048.           useState();
    
  1049.         }
    
  1050.       `,
    
  1051.       errors: [asyncComponentHookError('useState')],
    
  1052.     },
    
  1053.     {
    
  1054.       code: normalizeIndent`
    
  1055.         async function useAsyncHook() {
    
  1056.           useState();
    
  1057.         }
    
  1058.       `,
    
  1059.       errors: [asyncComponentHookError('useState')],
    
  1060.     },
    
  1061.   ],
    
  1062. };
    
  1063. 
    
  1064. if (__EXPERIMENTAL__) {
    
  1065.   tests.valid = [
    
  1066.     ...tests.valid,
    
  1067.     {
    
  1068.       code: normalizeIndent`
    
  1069.         // Valid because functions created with useEffectEvent can be called in a useEffect.
    
  1070.         function MyComponent({ theme }) {
    
  1071.           const onClick = useEffectEvent(() => {
    
  1072.             showNotification(theme);
    
  1073.           });
    
  1074.           useEffect(() => {
    
  1075.             onClick();
    
  1076.           });
    
  1077.         }
    
  1078.       `,
    
  1079.     },
    
  1080.     {
    
  1081.       code: normalizeIndent`
    
  1082.         // Valid because functions created with useEffectEvent can be called in closures.
    
  1083.         function MyComponent({ theme }) {
    
  1084.           const onClick = useEffectEvent(() => {
    
  1085.             showNotification(theme);
    
  1086.           });
    
  1087.           return <Child onClick={() => onClick()}></Child>;
    
  1088.         }
    
  1089.       `,
    
  1090.     },
    
  1091.     {
    
  1092.       code: normalizeIndent`
    
  1093.         // Valid because functions created with useEffectEvent can be called in closures.
    
  1094.         function MyComponent({ theme }) {
    
  1095.           const onClick = useEffectEvent(() => {
    
  1096.             showNotification(theme);
    
  1097.           });
    
  1098.           const onClick2 = () => { onClick() };
    
  1099.           const onClick3 = useCallback(() => onClick(), []);
    
  1100.           return <>
    
  1101.             <Child onClick={onClick2}></Child>
    
  1102.             <Child onClick={onClick3}></Child>
    
  1103.           </>;
    
  1104.         }
    
  1105.       `,
    
  1106.     },
    
  1107.     {
    
  1108.       code: normalizeIndent`
    
  1109.         // Valid because functions created with useEffectEvent can be passed by reference in useEffect
    
  1110.         // and useEffectEvent.
    
  1111.         function MyComponent({ theme }) {
    
  1112.           const onClick = useEffectEvent(() => {
    
  1113.             showNotification(theme);
    
  1114.           });
    
  1115.           const onClick2 = useEffectEvent(() => {
    
  1116.             debounce(onClick);
    
  1117.           });
    
  1118.           useEffect(() => {
    
  1119.             let id = setInterval(onClick, 100);
    
  1120.             return () => clearInterval(onClick);
    
  1121.           }, []);
    
  1122.           return <Child onClick={() => onClick2()} />
    
  1123.         }
    
  1124.       `,
    
  1125.     },
    
  1126.     {
    
  1127.       code: normalizeIndent`
    
  1128.         const MyComponent = ({theme}) => {
    
  1129.           const onClick = useEffectEvent(() => {
    
  1130.             showNotification(theme);
    
  1131.           });
    
  1132.           return <Child onClick={() => onClick()}></Child>;
    
  1133.         };
    
  1134.       `,
    
  1135.     },
    
  1136.     {
    
  1137.       code: normalizeIndent`
    
  1138.         function MyComponent({ theme }) {
    
  1139.           const notificationService = useNotifications();
    
  1140.           const showNotification = useEffectEvent((text) => {
    
  1141.             notificationService.notify(theme, text);
    
  1142.           });
    
  1143.           const onClick = useEffectEvent((text) => {
    
  1144.             showNotification(text);
    
  1145.           });
    
  1146.           return <Child onClick={(text) => onClick(text)} />
    
  1147.         }
    
  1148.       `,
    
  1149.     },
    
  1150.     {
    
  1151.       code: normalizeIndent`
    
  1152.         function MyComponent({ theme }) {
    
  1153.           useEffect(() => {
    
  1154.             onClick();
    
  1155.           });
    
  1156.           const onClick = useEffectEvent(() => {
    
  1157.             showNotification(theme);
    
  1158.           });
    
  1159.         }
    
  1160.       `,
    
  1161.     },
    
  1162.     {
    
  1163.       code: normalizeIndent`
    
  1164.         function App() {
    
  1165.           const text = use(Promise.resolve('A'));
    
  1166.           return <Text text={text} />
    
  1167.         }
    
  1168.       `,
    
  1169.     },
    
  1170.     {
    
  1171.       code: normalizeIndent`
    
  1172.         function App() {
    
  1173.           if (shouldShowText) {
    
  1174.             const text = use(query);
    
  1175.             return <Text text={text} />
    
  1176.           }
    
  1177.           return <Text text={shouldFetchBackupText ? use(backupQuery) : "Nothing to see here"} />
    
  1178.         }
    
  1179.       `,
    
  1180.     },
    
  1181.     {
    
  1182.       code: normalizeIndent`
    
  1183.         function App() {
    
  1184.           let data = [];
    
  1185.           for (const query of queries) {
    
  1186.             const text = use(item);
    
  1187.             data.push(text);
    
  1188.           }
    
  1189.           return <Child data={data} />
    
  1190.         }
    
  1191.       `,
    
  1192.     },
    
  1193.     {
    
  1194.       code: normalizeIndent`
    
  1195.         function App() {
    
  1196.           const data = someCallback((x) => use(x));
    
  1197.           return <Child data={data} />
    
  1198.         }
    
  1199.       `,
    
  1200.     },
    
  1201.   ];
    
  1202.   tests.invalid = [
    
  1203.     ...tests.invalid,
    
  1204.     {
    
  1205.       code: normalizeIndent`
    
  1206.         function MyComponent({ theme }) {
    
  1207.           const onClick = useEffectEvent(() => {
    
  1208.             showNotification(theme);
    
  1209.           });
    
  1210.           return <Child onClick={onClick}></Child>;
    
  1211.         }
    
  1212.       `,
    
  1213.       errors: [useEffectEventError('onClick')],
    
  1214.     },
    
  1215.     {
    
  1216.       code: normalizeIndent`
    
  1217.         // This should error even though it shares an identifier name with the below
    
  1218.         function MyComponent({theme}) {
    
  1219.           const onClick = useEffectEvent(() => {
    
  1220.             showNotification(theme)
    
  1221.           });
    
  1222.           return <Child onClick={onClick} />
    
  1223.         }
    
  1224. 
    
  1225.         // The useEffectEvent function shares an identifier name with the above
    
  1226.         function MyOtherComponent({theme}) {
    
  1227.           const onClick = useEffectEvent(() => {
    
  1228.             showNotification(theme)
    
  1229.           });
    
  1230.           return <Child onClick={() => onClick()} />
    
  1231.         }
    
  1232.       `,
    
  1233.       errors: [{...useEffectEventError('onClick'), line: 7}],
    
  1234.     },
    
  1235.     {
    
  1236.       code: normalizeIndent`
    
  1237.         const MyComponent = ({ theme }) => {
    
  1238.           const onClick = useEffectEvent(() => {
    
  1239.             showNotification(theme);
    
  1240.           });
    
  1241.           return <Child onClick={onClick}></Child>;
    
  1242.         }
    
  1243.       `,
    
  1244.       errors: [useEffectEventError('onClick')],
    
  1245.     },
    
  1246.     {
    
  1247.       code: normalizeIndent`
    
  1248.         // Invalid because onClick is being aliased to foo but not invoked
    
  1249.         function MyComponent({ theme }) {
    
  1250.           const onClick = useEffectEvent(() => {
    
  1251.             showNotification(theme);
    
  1252.           });
    
  1253.           let foo = onClick;
    
  1254.           return <Bar onClick={foo} />
    
  1255.         }
    
  1256.       `,
    
  1257.       errors: [{...useEffectEventError('onClick'), line: 7}],
    
  1258.     },
    
  1259.     {
    
  1260.       code: normalizeIndent`
    
  1261.         // Should error because it's being passed down to JSX, although it's been referenced once
    
  1262.         // in an effect
    
  1263.         function MyComponent({ theme }) {
    
  1264.           const onClick = useEffectEvent(() => {
    
  1265.             showNotification(them);
    
  1266.           });
    
  1267.           useEffect(() => {
    
  1268.             setTimeout(onClick, 100);
    
  1269.           });
    
  1270.           return <Child onClick={onClick} />
    
  1271.         }
    
  1272.       `,
    
  1273.       errors: [useEffectEventError('onClick')],
    
  1274.     },
    
  1275.     {
    
  1276.       code: normalizeIndent`
    
  1277.         Hook.use();
    
  1278.         Hook._use();
    
  1279.         Hook.useState();
    
  1280.         Hook._useState();
    
  1281.         Hook.use42();
    
  1282.         Hook.useHook();
    
  1283.         Hook.use_hook();
    
  1284.       `,
    
  1285.       errors: [
    
  1286.         topLevelError('Hook.use'),
    
  1287.         topLevelError('Hook.useState'),
    
  1288.         topLevelError('Hook.use42'),
    
  1289.         topLevelError('Hook.useHook'),
    
  1290.       ],
    
  1291.     },
    
  1292.     {
    
  1293.       code: normalizeIndent`
    
  1294.         function notAComponent() {
    
  1295.           use(promise);
    
  1296.         }
    
  1297.       `,
    
  1298.       errors: [functionError('use', 'notAComponent')],
    
  1299.     },
    
  1300.     {
    
  1301.       code: normalizeIndent`
    
  1302.         const text = use(promise);
    
  1303.         function App() {
    
  1304.           return <Text text={text} />
    
  1305.         }
    
  1306.       `,
    
  1307.       errors: [topLevelError('use')],
    
  1308.     },
    
  1309.     {
    
  1310.       code: normalizeIndent`
    
  1311.         class C {
    
  1312.           m() {
    
  1313.             use(promise);
    
  1314.           }
    
  1315.         }
    
  1316.       `,
    
  1317.       errors: [classError('use')],
    
  1318.     },
    
  1319.     {
    
  1320.       code: normalizeIndent`
    
  1321.         async function AsyncComponent() {
    
  1322.           use();
    
  1323.         }
    
  1324.       `,
    
  1325.       errors: [asyncComponentHookError('use')],
    
  1326.     },
    
  1327.   ];
    
  1328. }
    
  1329. 
    
  1330. function conditionalError(hook, hasPreviousFinalizer = false) {
    
  1331.   return {
    
  1332.     message:
    
  1333.       `React Hook "${hook}" is called conditionally. React Hooks must be ` +
    
  1334.       'called in the exact same order in every component render.' +
    
  1335.       (hasPreviousFinalizer
    
  1336.         ? ' Did you accidentally call a React Hook after an early return?'
    
  1337.         : ''),
    
  1338.   };
    
  1339. }
    
  1340. 
    
  1341. function loopError(hook) {
    
  1342.   return {
    
  1343.     message:
    
  1344.       `React Hook "${hook}" may be executed more than once. Possibly ` +
    
  1345.       'because it is called in a loop. React Hooks must be called in the ' +
    
  1346.       'exact same order in every component render.',
    
  1347.   };
    
  1348. }
    
  1349. 
    
  1350. function functionError(hook, fn) {
    
  1351.   return {
    
  1352.     message:
    
  1353.       `React Hook "${hook}" is called in function "${fn}" that is neither ` +
    
  1354.       'a React function component nor a custom React Hook function.' +
    
  1355.       ' React component names must start with an uppercase letter.' +
    
  1356.       ' React Hook names must start with the word "use".',
    
  1357.   };
    
  1358. }
    
  1359. 
    
  1360. function genericError(hook) {
    
  1361.   return {
    
  1362.     message:
    
  1363.       `React Hook "${hook}" cannot be called inside a callback. React Hooks ` +
    
  1364.       'must be called in a React function component or a custom React ' +
    
  1365.       'Hook function.',
    
  1366.   };
    
  1367. }
    
  1368. 
    
  1369. function topLevelError(hook) {
    
  1370.   return {
    
  1371.     message:
    
  1372.       `React Hook "${hook}" cannot be called at the top level. React Hooks ` +
    
  1373.       'must be called in a React function component or a custom React ' +
    
  1374.       'Hook function.',
    
  1375.   };
    
  1376. }
    
  1377. 
    
  1378. function classError(hook) {
    
  1379.   return {
    
  1380.     message:
    
  1381.       `React Hook "${hook}" cannot be called in a class component. React Hooks ` +
    
  1382.       'must be called in a React function component or a custom React ' +
    
  1383.       'Hook function.',
    
  1384.   };
    
  1385. }
    
  1386. 
    
  1387. function useEffectEventError(fn) {
    
  1388.   return {
    
  1389.     message:
    
  1390.       `\`${fn}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
    
  1391.       'the same component. They cannot be assigned to variables or passed down.',
    
  1392.   };
    
  1393. }
    
  1394. 
    
  1395. function asyncComponentHookError(fn) {
    
  1396.   return {
    
  1397.     message: `React Hook "${fn}" cannot be called in an async function.`,
    
  1398.   };
    
  1399. }
    
  1400. 
    
  1401. // For easier local testing
    
  1402. if (!process.env.CI) {
    
  1403.   let only = [];
    
  1404.   let skipped = [];
    
  1405.   [...tests.valid, ...tests.invalid].forEach(t => {
    
  1406.     if (t.skip) {
    
  1407.       delete t.skip;
    
  1408.       skipped.push(t);
    
  1409.     }
    
  1410.     if (t.only) {
    
  1411.       delete t.only;
    
  1412.       only.push(t);
    
  1413.     }
    
  1414.   });
    
  1415.   const predicate = t => {
    
  1416.     if (only.length > 0) {
    
  1417.       return only.indexOf(t) !== -1;
    
  1418.     }
    
  1419.     if (skipped.length > 0) {
    
  1420.       return skipped.indexOf(t) === -1;
    
  1421.     }
    
  1422.     return true;
    
  1423.   };
    
  1424.   tests.valid = tests.valid.filter(predicate);
    
  1425.   tests.invalid = tests.invalid.filter(predicate);
    
  1426. }
    
  1427. 
    
  1428. const eslintTester = new ESLintTester();
    
  1429. eslintTester.run('react-hooks', ReactHooksESLintRule, tests);