DevTools: Update named hooks match to use column number also (#21833)

This prevents edge cases where AST nodes are incorrectly matched.
This commit is contained in:
Brian Vaughn 2021-07-08 16:12:22 -04:00 committed by GitHub
parent 92af60afb2
commit f52b73f9d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 324 additions and 76 deletions

View File

@ -21,6 +21,7 @@ packages/react-devtools-extensions/chrome/build
packages/react-devtools-extensions/firefox/build
packages/react-devtools-extensions/shared/build
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/
packages/react-devtools-extensions/src/__tests__/__source__/__untransformed__/
packages/react-devtools-extensions/src/ErrorTesterCompiled.js
packages/react-devtools-inline/dist
packages/react-devtools-shell/dist

View File

@ -3,6 +3,7 @@ packages/react-devtools-extensions/chrome/build
packages/react-devtools-extensions/firefox/build
packages/react-devtools-extensions/shared/build
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/
packages/react-devtools-extensions/src/__tests__/__source__/__untransformed__/
packages/react-devtools-extensions/src/ErrorTesterCompiled.js
packages/react-devtools-inline/dist
packages/react-devtools-shell/dist

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import {createContext, useContext} from 'react';
const A = createContext(1);
const B = createContext(2);
export function Component() {
const a = useContext(A);
const b = useContext(B);
// prettier-ignore
const c = useContext(A), d = useContext(B); // eslint-disable-line one-var
return a + b + c + d;
}

View File

@ -71,7 +71,27 @@ function Component$1() {
*
* @flow
*/
const A = /*#__PURE__*/React.createContext(1);
const B = /*#__PURE__*/React.createContext(2);
function Component$2() {
const a = React.useContext(A);
const b = React.useContext(B); // prettier-ignore
const c = React.useContext(A),
d = React.useContext(B); // eslint-disable-line one-var
return a + b + c + d;
}
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
function Component$3() {
const [count, setCount] = React.useState(0);
return /*#__PURE__*/React__default.createElement("div", null, /*#__PURE__*/React__default.createElement("p", null, "You clicked ", count, " times"), /*#__PURE__*/React__default.createElement("button", {
onClick: () => setCount(count + 1)
@ -86,7 +106,7 @@ function Component$2() {
*
* @flow
*/
function Component$3() {
function Component$4() {
const [count] = require('react').useState(0);
return count;
@ -191,8 +211,9 @@ var ToDoList = /*#__PURE__*/Object.freeze({
exports.ComponentWithCustomHook = Component;
exports.ComponentWithExternalCustomHooks = Component$1;
exports.Example = Component$2;
exports.InlineRequire = Component$3;
exports.ComponentWithMultipleHooksPerLine = Component$2;
exports.Example = Component$3;
exports.InlineRequire = Component$4;
exports.ToDoList = ToDoList;
exports.useTheme = useTheme;
//# sourceMappingURL=index.js.map

View File

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Component = Component;
var _react = require("react");
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const A = /*#__PURE__*/(0, _react.createContext)(1);
const B = /*#__PURE__*/(0, _react.createContext)(2);
function Component() {
const a = (0, _react.useContext)(A);
const b = (0, _react.useContext)(B); // prettier-ignore
const c = (0, _react.useContext)(A),
d = (0, _react.useContext)(B); // eslint-disable-line one-var
return a + b + c + d;
}
//# sourceMappingURL=ComponentWithMultipleHooksPerLine.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["ComponentWithMultipleHooksPerLine.js"],"names":["A","B","Component","a","b","c","d"],"mappings":";;;;;;;AASA;;AATA;;;;;;;;AAWA,MAAMA,CAAC,gBAAG,0BAAc,CAAd,CAAV;AACA,MAAMC,CAAC,gBAAG,0BAAc,CAAd,CAAV;;AAEO,SAASC,SAAT,GAAqB;AAC1B,QAAMC,CAAC,GAAG,uBAAWH,CAAX,CAAV;AACA,QAAMI,CAAC,GAAG,uBAAWH,CAAX,CAAV,CAF0B,CAI1B;;AACA,QAAMI,CAAC,GAAG,uBAAWL,CAAX,CAAV;AAAA,QAAyBM,CAAC,GAAG,uBAAWL,CAAX,CAA7B,CAL0B,CAKkB;;AAE5C,SAAOE,CAAC,GAAGC,CAAJ,GAAQC,CAAR,GAAYC,CAAnB;AACD","sourcesContent":["/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @flow\n */\n\nimport {createContext, useContext} from 'react';\n\nconst A = createContext(1);\nconst B = createContext(2);\n\nexport function Component() {\n const a = useContext(A);\n const b = useContext(B);\n\n // prettier-ignore\n const c = useContext(A), d = useContext(B); // eslint-disable-line one-var\n\n return a + b + c + d;\n}\n"]}

View File

@ -15,6 +15,12 @@ Object.defineProperty(exports, "ComponentWithExternalCustomHooks", {
return _ComponentWithExternalCustomHooks.Component;
}
});
Object.defineProperty(exports, "ComponentWithMultipleHooksPerLine", {
enumerable: true,
get: function () {
return _ComponentWithMultipleHooksPerLine.Component;
}
});
Object.defineProperty(exports, "Example", {
enumerable: true,
get: function () {
@ -39,6 +45,8 @@ var _ComponentWithCustomHook = require("./ComponentWithCustomHook");
var _ComponentWithExternalCustomHooks = require("./ComponentWithExternalCustomHooks");
var _ComponentWithMultipleHooksPerLine = require("./ComponentWithMultipleHooksPerLine");
var _Example = require("./Example");
var _InlineRequire = require("./InlineRequire");

View File

@ -1 +1 @@
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA;;AACA;;AACA;;AACA;;AACA;;;;AAEA","sourcesContent":["/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @flow\n */\n\nexport {Component as ComponentWithCustomHook} from './ComponentWithCustomHook';\nexport {Component as ComponentWithExternalCustomHooks} from './ComponentWithExternalCustomHooks';\nexport {Component as Example} from './Example';\nexport {Component as InlineRequire} from './InlineRequire';\nimport * as ToDoList from './ToDoList';\nexport {ToDoList};\nexport {default as useTheme} from './useTheme';\n"]}
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;AAEA","sourcesContent":["/**\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @flow\n */\n\nexport {Component as ComponentWithCustomHook} from './ComponentWithCustomHook';\nexport {Component as ComponentWithExternalCustomHooks} from './ComponentWithExternalCustomHooks';\nexport {Component as ComponentWithMultipleHooksPerLine} from './ComponentWithMultipleHooksPerLine';\nexport {Component as Example} from './Example';\nexport {Component as InlineRequire} from './InlineRequire';\nimport * as ToDoList from './ToDoList';\nexport {ToDoList};\nexport {default as useTheme} from './useTheme';\n"]}

View File

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Component = Component;
var _react = require("react");
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const A = /*#__PURE__*/(0, _react.createContext)(1);
const B = /*#__PURE__*/(0, _react.createContext)(2);
function Component() {
const a = (0, _react.useContext)(A);
const b = (0, _react.useContext)(B); // prettier-ignore
const c = (0, _react.useContext)(A),
d = (0, _react.useContext)(B); // eslint-disable-line one-var
return a + b + c + d;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbXBvbmVudFdpdGhNdWx0aXBsZUhvb2tzUGVyTGluZS5qcyJdLCJuYW1lcyI6WyJBIiwiQiIsIkNvbXBvbmVudCIsImEiLCJiIiwiYyIsImQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7QUFTQTs7QUFUQTs7Ozs7Ozs7QUFXQSxNQUFNQSxDQUFDLGdCQUFHLDBCQUFjLENBQWQsQ0FBVjtBQUNBLE1BQU1DLENBQUMsZ0JBQUcsMEJBQWMsQ0FBZCxDQUFWOztBQUVPLFNBQVNDLFNBQVQsR0FBcUI7QUFDMUIsUUFBTUMsQ0FBQyxHQUFHLHVCQUFXSCxDQUFYLENBQVY7QUFDQSxRQUFNSSxDQUFDLEdBQUcsdUJBQVdILENBQVgsQ0FBVixDQUYwQixDQUkxQjs7QUFDQSxRQUFNSSxDQUFDLEdBQUcsdUJBQVdMLENBQVgsQ0FBVjtBQUFBLFFBQXlCTSxDQUFDLEdBQUcsdUJBQVdMLENBQVgsQ0FBN0IsQ0FMMEIsQ0FLa0I7O0FBRTVDLFNBQU9FLENBQUMsR0FBR0MsQ0FBSixHQUFRQyxDQUFSLEdBQVlDLENBQW5CO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAoYykgRmFjZWJvb2ssIEluYy4gYW5kIGl0cyBhZmZpbGlhdGVzLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLlxuICpcbiAqIEBmbG93XG4gKi9cblxuaW1wb3J0IHtjcmVhdGVDb250ZXh0LCB1c2VDb250ZXh0fSBmcm9tICdyZWFjdCc7XG5cbmNvbnN0IEEgPSBjcmVhdGVDb250ZXh0KDEpO1xuY29uc3QgQiA9IGNyZWF0ZUNvbnRleHQoMik7XG5cbmV4cG9ydCBmdW5jdGlvbiBDb21wb25lbnQoKSB7XG4gIGNvbnN0IGEgPSB1c2VDb250ZXh0KEEpO1xuICBjb25zdCBiID0gdXNlQ29udGV4dChCKTtcblxuICAvLyBwcmV0dGllci1pZ25vcmVcbiAgY29uc3QgYyA9IHVzZUNvbnRleHQoQSksIGQgPSB1c2VDb250ZXh0KEIpOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG9uZS12YXJcblxuICByZXR1cm4gYSArIGIgKyBjICsgZDtcbn1cbiJdfQ==

View File

@ -15,6 +15,12 @@ Object.defineProperty(exports, "ComponentWithExternalCustomHooks", {
return _ComponentWithExternalCustomHooks.Component;
}
});
Object.defineProperty(exports, "ComponentWithMultipleHooksPerLine", {
enumerable: true,
get: function () {
return _ComponentWithMultipleHooksPerLine.Component;
}
});
Object.defineProperty(exports, "Example", {
enumerable: true,
get: function () {
@ -39,6 +45,8 @@ var _ComponentWithCustomHook = require("./ComponentWithCustomHook");
var _ComponentWithExternalCustomHooks = require("./ComponentWithExternalCustomHooks");
var _ComponentWithMultipleHooksPerLine = require("./ComponentWithMultipleHooksPerLine");
var _Example = require("./Example");
var _InlineRequire = require("./InlineRequire");
@ -54,4 +62,4 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFTQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7OztBQUVBIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmV4cG9ydCB7Q29tcG9uZW50IGFzIENvbXBvbmVudFdpdGhDdXN0b21Ib29rfSBmcm9tICcuL0NvbXBvbmVudFdpdGhDdXN0b21Ib29rJztcbmV4cG9ydCB7Q29tcG9uZW50IGFzIENvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzfSBmcm9tICcuL0NvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzJztcbmV4cG9ydCB7Q29tcG9uZW50IGFzIEV4YW1wbGV9IGZyb20gJy4vRXhhbXBsZSc7XG5leHBvcnQge0NvbXBvbmVudCBhcyBJbmxpbmVSZXF1aXJlfSBmcm9tICcuL0lubGluZVJlcXVpcmUnO1xuaW1wb3J0ICogYXMgVG9Eb0xpc3QgZnJvbSAnLi9Ub0RvTGlzdCc7XG5leHBvcnQge1RvRG9MaXN0fTtcbmV4cG9ydCB7ZGVmYXVsdCBhcyB1c2VUaGVtZX0gZnJvbSAnLi91c2VUaGVtZSc7XG4iXX0=
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFTQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7OztBQUVBIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmV4cG9ydCB7Q29tcG9uZW50IGFzIENvbXBvbmVudFdpdGhDdXN0b21Ib29rfSBmcm9tICcuL0NvbXBvbmVudFdpdGhDdXN0b21Ib29rJztcbmV4cG9ydCB7Q29tcG9uZW50IGFzIENvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzfSBmcm9tICcuL0NvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzJztcbmV4cG9ydCB7Q29tcG9uZW50IGFzIENvbXBvbmVudFdpdGhNdWx0aXBsZUhvb2tzUGVyTGluZX0gZnJvbSAnLi9Db21wb25lbnRXaXRoTXVsdGlwbGVIb29rc1BlckxpbmUnO1xuZXhwb3J0IHtDb21wb25lbnQgYXMgRXhhbXBsZX0gZnJvbSAnLi9FeGFtcGxlJztcbmV4cG9ydCB7Q29tcG9uZW50IGFzIElubGluZVJlcXVpcmV9IGZyb20gJy4vSW5saW5lUmVxdWlyZSc7XG5pbXBvcnQgKiBhcyBUb0RvTGlzdCBmcm9tICcuL1RvRG9MaXN0JztcbmV4cG9ydCB7VG9Eb0xpc3R9O1xuZXhwb3J0IHtkZWZhdWx0IGFzIHVzZVRoZW1lfSBmcm9tICcuL3VzZVRoZW1lJztcbiJdfQ==

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const {useDebugValue, useState} = require('react');
function Component(props) {
const foo = useCustomHookOne();
// This cae is ignored;
// the meaning of a tuple assignment for a custom hook is unclear.
const [bar] = useCustomHookTwo();
return `${foo}-${bar}`;
}
function useCustomHookOne() {
// DebugValue hook should not appear in log.
useDebugValue('example');
return true;
}
function useCustomHookTwo() {
const [baz, setBaz] = useState(true);
return [baz, setBaz];
}
module.exports = {Component};

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const {useDebugValue} = require('react');
function Component(props) {
useCustomHookOne();
const [bar] = useCustomHookTwo();
return bar;
}
function useCustomHookOne() {
// DebugValue hook should not appear in log.
useDebugValue('example');
}
function useCustomHookTwo() {
// DebugValue hook should not appear in log.
useDebugValue('example');
return [true];
}
module.exports = {Component};

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const React = require('react');
const {useEffect} = React;
function Component(props) {
useEffect(() => {});
React.useLayoutEffect(() => () => {});
return null;
}
module.exports = {Component};

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const React = require('react');
const {useReducer} = React;
function Component(props) {
const [foo] = useReducer(true);
const [bar] = useReducer(true);
const [baz] = React.useReducer(true);
return `${foo}-${bar}-${baz}`;
}
module.exports = {Component};

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
const React = require('react');
const {useState} = React;
function Component(props) {
const [foo] = useState(true);
const bar = useState(true);
const [baz] = React.useState(true);
return `${foo}-${bar}-${baz}`;
}
module.exports = {Component};

View File

@ -0,0 +1 @@
The JavaScript source files in this directory are not linted or pre-processed in any way. This is intentional, since they are used by the `parseHookNames-test` to test the behavior of "uncompiled" JavaScript (without source maps).

View File

@ -9,6 +9,7 @@
export {Component as ComponentWithCustomHook} from './ComponentWithCustomHook';
export {Component as ComponentWithExternalCustomHooks} from './ComponentWithExternalCustomHooks';
export {Component as ComponentWithMultipleHooksPerLine} from './ComponentWithMultipleHooksPerLine';
export {Component as Example} from './Example';
export {Component as InlineRequire} from './InlineRequire';
import * as ToDoList from './ToDoList';

View File

@ -7,6 +7,10 @@
* @flow
*/
// Note that this test uses React components declared in the "__source__" directory.
// This is done to control if and how the code is transformed at runtime.
// Do not declare test components within this test file as it is very fragile.
describe('parseHookNames', () => {
let fetchMock;
let inspectHooks;
@ -87,65 +91,29 @@ describe('parseHookNames', () => {
}
it('should parse names for useState()', async () => {
const React = require('react');
const {useState} = React;
function Component(props) {
const [foo] = useState(true);
const bar = useState(true);
const [baz] = React.useState(true);
return `${foo}-${bar}-${baz}`;
}
const Component = require('./__source__/__untransformed__/ComponentWithUseState')
.Component;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, ['foo', 'bar', 'baz']);
});
it('should parse names for useReducer()', async () => {
const React = require('react');
const {useReducer} = React;
function Component(props) {
const [foo] = useReducer(true);
const [bar] = useReducer(true);
const [baz] = React.useReducer(true);
return `${foo}-${bar}-${baz}`;
}
const Component = require('./__source__/__untransformed__/ComponentWithUseReducer')
.Component;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, ['foo', 'bar', 'baz']);
});
it('should return null for hooks without names like useEffect', async () => {
const React = require('react');
const {useEffect} = React;
function Component(props) {
useEffect(() => {});
React.useLayoutEffect(() => () => {});
return null;
}
const Component = require('./__source__/__untransformed__/ComponentWithUseEffect')
.Component;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, []); // No hooks with names
});
it('should parse names for custom hooks', async () => {
const {useDebugValue, useState} = require('react');
function useCustomHookOne() {
// DebugValue hook should not appear in log.
useDebugValue('example');
return true;
}
function useCustomHookTwo() {
const [baz, setBaz] = useState(true);
return [baz, setBaz];
}
function Component(props) {
const foo = useCustomHookOne();
// This cae is ignored;
// the meaning of a tuple assignment for a custom hook is unclear.
const [bar] = useCustomHookTwo();
return `${foo}-${bar}`;
}
const Component = require('./__source__/__untransformed__/ComponentWithNamedCustomHooks')
.Component;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, [
'foo',
@ -155,22 +123,8 @@ describe('parseHookNames', () => {
});
it('should return null for custom hooks without explicit names', async () => {
const {useDebugValue} = require('react');
function useCustomHookOne() {
// DebugValue hook should not appear in log.
useDebugValue('example');
}
function useCustomHookTwo() {
// DebugValue hook should not appear in log.
useDebugValue('example');
return [true];
}
function Component(props) {
useCustomHookOne();
const [bar] = useCustomHookTwo();
return bar;
}
const Component = require('./__source__/__untransformed__/ComponentWithUnnamedCustomHooks')
.Component;
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, [
null, // Custom hooks can have names, but this one does not even return a value.
@ -267,6 +221,30 @@ describe('parseHookNames', () => {
); // bundle source map
});
it('should work when multiple hooks are on a line', async () => {
async function test(path, name = 'Component') {
const Component = require(path)[name];
const hookNames = await getHookNamesForComponent(Component);
expectHookNamesToEqual(hookNames, [
'a', // useContext()
'b', // useContext()
'c', // useContext()
'd', // useContext()
]);
}
await test(
'./__source__/__compiled__/inline/ComponentWithMultipleHooksPerLine',
); // inline source map
await test(
'./__source__/__compiled__/external/ComponentWithMultipleHooksPerLine',
); // external source map
await test(
'./__source__/__compiled__/bundle',
'ComponentWithMultipleHooksPerLine',
); // bundle source map
});
// TODO Inline require (e.g. require("react").useState()) isn't supported yet.
// Maybe this isn't an important use case to support,
// since inline requires are most likely to exist in compiled source (if at all).

View File

@ -29,9 +29,25 @@ const AST_NODE_TYPES = Object.freeze({
});
// Check if line number obtained from source map and the line number in hook node match
function checkNodeLocation(path: NodePath, line: number): boolean {
function checkNodeLocation(
path: NodePath,
line: number,
column: number,
): boolean {
const {start, end} = path.node.loc;
return line >= start.line && line <= end.line;
if (line < start.line || line > end.line) {
return false;
}
if (
(line === start.line && column < start.column) ||
(line === end.line && column > end.column)
) {
return false;
}
return true;
}
// Checks whether hookNode is a member of targetHookNode
@ -91,13 +107,15 @@ export function getHookName(
originalSourceAST: mixed,
originalSourceCode: string,
originalSourceLineNumber: number,
originalSourceColumnNumber: number,
): string | null {
const hooksFromAST = getPotentialHookDeclarationsFromAST(originalSourceAST);
const potentialReactHookASTNode = hooksFromAST.find(node => {
const nodeLocationCheck = checkNodeLocation(
node,
((originalSourceLineNumber: any): number),
originalSourceLineNumber,
originalSourceColumnNumber,
);
const hookDeclaractionCheck = isConfirmedHookDeclaration(node);
return nodeLocationCheck && hookDeclaractionCheck;

View File

@ -376,32 +376,37 @@ function findHookNames(
const sourceConsumer = hookSourceData.sourceConsumer;
let originalSourceColumnNumber;
let originalSourceLineNumber;
if (areSourceMapsAppliedToErrors() || !sourceConsumer) {
// Either the current environment automatically applies source maps to errors,
// or the current code had no source map to begin with.
// Either way, we don't need to convert the Error stack frame locations.
originalSourceColumnNumber = columnNumber;
originalSourceLineNumber = lineNumber;
} else {
originalSourceLineNumber = sourceConsumer.originalPositionFor({
const position = sourceConsumer.originalPositionFor({
line: lineNumber,
// Column numbers are representated differently between tools/engines.
// For more info see https://github.com/facebook/react/issues/21792#issuecomment-873171991
column: columnNumber - 1,
}).line;
});
originalSourceColumnNumber = position.column;
originalSourceLineNumber = position.line;
}
if (__DEBUG__) {
console.log(
'findHookNames() mapped line number',
lineNumber,
'to',
originalSourceLineNumber,
`findHookNames() mapped line ${lineNumber}->${originalSourceLineNumber} and column ${columnNumber}->${originalSourceColumnNumber}`,
);
}
if (originalSourceLineNumber === null) {
if (
originalSourceLineNumber === null ||
originalSourceColumnNumber === null
) {
return null;
}
@ -410,6 +415,7 @@ function findHookNames(
hookSourceData.originalSourceAST,
((hookSourceData.originalSourceCode: any): string),
((originalSourceLineNumber: any): number),
originalSourceColumnNumber,
);
if (__DEBUG__) {

View File

@ -59,6 +59,7 @@ module.exports = Object.assign({}, baseConfig, {
'/node_modules/',
'<rootDir>/build2/',
'/__compiled__/',
'/__untransformed__/',
],
testRegex:
'packages/react-devtools-(extensions|shared)/src/__tests__/[^]+.test.js$',

View File

@ -30,6 +30,7 @@ const files = glob
'**/node_modules/**',
'**/cjs/**',
'**/__compiled__/**',
'**/__untransformed__/**',
'packages/react-devtools-extensions/src/ErrorTesterCompiled.js',
],
})