@samitouri / QOS-React-2 / commits / 9c7f29ebe7

[WIP] DevTools: Support named hooks for >1 module in a bundle (#21790)

* Add named hooks test case built with Rollup * Fix prepareStackTrace unpatching, remove sourceURL * Prettier * Resolve source map URL/path relative to the script * Add failing tests for multi-module bundle * Parse hook names from multiple modules in a bundle * Create a HookSourceData per location key (file, line, column). * Cache the source map per runtime URL ( = file part of location key). * Don't store sourceMapContents - only store a consumer instance. * Look up original source URLs in the source map correctly. * Cache the code + AST per original URL. * Fix off-by-one column number lookup. * Some naming and typing tweaks related to the above. * Stop storing the consumer outside the with() callback, which is a bug. * Lint fix for 8d8dd25 * Added devDependencies to react-devtools-extensions package.json * Added some debug logging and TODO comments * Added additional DEBUG logging to hook names cache Co-authored-by: Brian Vaughn <bvaughn@fb.com>

Moti Zilberman committed Jul 7, 2021 at 18:07 UTC 9c7f29ebe75a7546e59038123d3b0c3515bec916
25 files changed +694 -222
packages/react-devtools-extensions/package.json
+5
@@ -19,6 +19,7 @@
19 "update-mock-source-maps": "node ./src/__tests__/updateMockSourceMaps.js"
20 },
21 "devDependencies": {
22 + "acorn-jsx": "^5.2.0",
23 "@babel/core": "^7.11.1",
24 "@babel/plugin-proposal-class-properties": "^7.10.4",
25 "@babel/plugin-transform-flow-strip-types": "^7.10.4",
@@ -44,6 +45,10 @@
45 "open": "^7.0.2",
46 "os-name": "^3.1.0",
47 "raw-loader": "^3.1.0",
48 + "rollup": "^1.19.4",
49 + "rollup-plugin-babel": "^4.0.1",
50 + "rollup-plugin-commonjs": "^9.3.4",
51 + "rollup-plugin-node-resolve": "^2.1.1",
52 "source-map": "^0.8.0-beta.0",
53 "style-loader": "^0.23.1",
54 "web-ext": "^3.0.0",
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/bundle/index.js new
+198
@@ -0,0 +1,198 @@
1 +'use strict';
2 +
3 +Object.defineProperty(exports, '__esModule', { value: true });
4 +
5 +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6 +
7 +var React = require('react');
8 +var React__default = _interopDefault(React);
9 +
10 +/**
11 + * Copyright (c) Facebook, Inc. and its affiliates.
12 + *
13 + * This source code is licensed under the MIT license found in the
14 + * LICENSE file in the root directory of this source tree.
15 + *
16 + * @flow
17 + */
18 +function Component() {
19 + const [count, setCount] = React.useState(0);
20 + const isDarkMode = useIsDarkMode();
21 + React.useEffect(() => {// ...
22 + }, []);
23 +
24 + const handleClick = () => setCount(count + 1);
25 +
26 + return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("div", null, "Dark mode? ", isDarkMode), /*#__PURE__*/React__default.createElement("div", null, "Count: ", count), /*#__PURE__*/React__default.createElement("button", {
27 + onClick: handleClick
28 + }, "Update count"));
29 +}
30 +
31 +function useIsDarkMode() {
32 + const [isDarkMode] = React.useState(false);
33 + React.useEffect(function useEffectCreate() {// Here is where we may listen to a "theme" event...
34 + }, []);
35 + return isDarkMode;
36 +}
37 +
38 +/**
39 + * Copyright (c) Facebook, Inc. and its affiliates.
40 + *
41 + * This source code is licensed under the MIT license found in the
42 + * LICENSE file in the root directory of this source tree.
43 + *
44 + * @flow
45 + */
46 +const ThemeContext = /*#__PURE__*/React.createContext('bright');
47 +function useTheme() {
48 + const theme = React.useContext(ThemeContext);
49 + React.useDebugValue(theme);
50 + return theme;
51 +}
52 +
53 +/**
54 + * Copyright (c) Facebook, Inc. and its affiliates.
55 + *
56 + * This source code is licensed under the MIT license found in the
57 + * LICENSE file in the root directory of this source tree.
58 + *
59 + * @flow
60 + */
61 +function Component$1() {
62 + const theme = useTheme();
63 + return /*#__PURE__*/React__default.createElement("div", null, "theme: ", theme);
64 +}
65 +
66 +/**
67 + * Copyright (c) Facebook, Inc. and its affiliates.
68 + *
69 + * This source code is licensed under the MIT license found in the
70 + * LICENSE file in the root directory of this source tree.
71 + *
72 + * @flow
73 + */
74 +function Component$2() {
75 + const [count, setCount] = React.useState(0);
76 + return /*#__PURE__*/React__default.createElement("div", null, /*#__PURE__*/React__default.createElement("p", null, "You clicked ", count, " times"), /*#__PURE__*/React__default.createElement("button", {
77 + onClick: () => setCount(count + 1)
78 + }, "Click me"));
79 +}
80 +
81 +/**
82 + * Copyright (c) Facebook, Inc. and its affiliates.
83 + *
84 + * This source code is licensed under the MIT license found in the
85 + * LICENSE file in the root directory of this source tree.
86 + *
87 + * @flow
88 + */
89 +function Component$3() {
90 + const [count] = require('react').useState(0);
91 +
92 + return count;
93 +}
94 +
95 +/**
96 + * Copyright (c) Facebook, Inc. and its affiliates.
97 + *
98 + * This source code is licensed under the MIT license found in the
99 + * LICENSE file in the root directory of this source tree.
100 + *
101 + * @flow
102 + */
103 +function ListItem({
104 + item,
105 + removeItem,
106 + toggleItem
107 +}) {
108 + const handleDelete = React.useCallback(() => {
109 + removeItem(item);
110 + }, [item, removeItem]);
111 + const handleToggle = React.useCallback(() => {
112 + toggleItem(item);
113 + }, [item, toggleItem]);
114 + return /*#__PURE__*/React.createElement("li", null, /*#__PURE__*/React.createElement("button", {
115 + onClick: handleDelete
116 + }, "Delete"), /*#__PURE__*/React.createElement("label", null, /*#__PURE__*/React.createElement("input", {
117 + checked: item.isComplete,
118 + onChange: handleToggle,
119 + type: "checkbox"
120 + }), ' ', item.text));
121 +}
122 +function List(props) {
123 + const [newItemText, setNewItemText] = React.useState('');
124 + const [items, setItems] = React.useState([{
125 + id: 1,
126 + isComplete: true,
127 + text: 'First'
128 + }, {
129 + id: 2,
130 + isComplete: true,
131 + text: 'Second'
132 + }, {
133 + id: 3,
134 + isComplete: false,
135 + text: 'Third'
136 + }]);
137 + const [uid, setUID] = React.useState(4);
138 + const handleClick = React.useCallback(() => {
139 + if (newItemText !== '') {
140 + setItems([...items, {
141 + id: uid,
142 + isComplete: false,
143 + text: newItemText
144 + }]);
145 + setUID(uid + 1);
146 + setNewItemText('');
147 + }
148 + }, [newItemText, items, uid]);
149 + const handleKeyPress = React.useCallback(event => {
150 + if (event.key === 'Enter') {
151 + handleClick();
152 + }
153 + }, [handleClick]);
154 + const handleChange = React.useCallback(event => {
155 + setNewItemText(event.currentTarget.value);
156 + }, [setNewItemText]);
157 + const removeItem = React.useCallback(itemToRemove => setItems(items.filter(item => item !== itemToRemove)), [items]);
158 + const toggleItem = React.useCallback(itemToToggle => {
159 + // Dont use indexOf()
160 + // because editing props in DevTools creates a new Object.
161 + const index = items.findIndex(item => item.id === itemToToggle.id);
162 + setItems(items.slice(0, index).concat({ ...itemToToggle,
163 + isComplete: !itemToToggle.isComplete
164 + }).concat(items.slice(index + 1)));
165 + }, [items]);
166 + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h1", null, "List"), /*#__PURE__*/React.createElement("input", {
167 + type: "text",
168 + placeholder: "New list item...",
169 + value: newItemText,
170 + onChange: handleChange,
171 + onKeyPress: handleKeyPress
172 + }), /*#__PURE__*/React.createElement("button", {
173 + disabled: newItemText === '',
174 + onClick: handleClick
175 + }, /*#__PURE__*/React.createElement("span", {
176 + role: "img",
177 + "aria-label": "Add item"
178 + }, "Add")), /*#__PURE__*/React.createElement("ul", null, items.map(item => /*#__PURE__*/React.createElement(ListItem, {
179 + key: item.id,
180 + item: item,
181 + removeItem: removeItem,
182 + toggleItem: toggleItem
183 + }))));
184 +}
185 +
186 +var ToDoList = /*#__PURE__*/Object.freeze({
187 + __proto__: null,
188 + ListItem: ListItem,
189 + List: List
190 +});
191 +
192 +exports.ComponentWithCustomHook = Component;
193 +exports.ComponentWithExternalCustomHooks = Component$1;
194 +exports.Example = Component$2;
195 +exports.InlineRequire = Component$3;
196 +exports.ToDoList = ToDoList;
197 +exports.useTheme = useTheme;
198 +//# sourceMappingURL=index.js.map
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/bundle/index.js.map new
+1
@@ -0,0 +1 @@
1 +{"version":3,"file":"index.js","sources":["../../ComponentWithCustomHook.js","../../useTheme.js","../../ComponentWithExternalCustomHooks.js","../../Example.js","../../InlineRequire.js","../../ToDoList.js"],"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 React, {useEffect, useState} from 'react';\n\nexport function Component() {\n const [count, setCount] = useState(0);\n const isDarkMode = useIsDarkMode();\n\n useEffect(() => {\n // ...\n }, []);\n\n const handleClick = () => setCount(count + 1);\n\n return (\n <>\n <div>Dark mode? {isDarkMode}</div>\n <div>Count: {count}</div>\n <button onClick={handleClick}>Update count</button>\n </>\n );\n}\n\nfunction useIsDarkMode() {\n const [isDarkMode] = useState(false);\n\n useEffect(function useEffectCreate() {\n // Here is where we may listen to a \"theme\" event...\n }, []);\n\n return isDarkMode;\n}\n","/**\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, useDebugValue} from 'react';\n\nexport const ThemeContext = createContext('bright');\n\nexport default function useTheme() {\n const theme = useContext(ThemeContext);\n useDebugValue(theme);\n return theme;\n}\n","/**\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 React from 'react';\nimport useTheme from './useTheme';\n\nexport function Component() {\n const theme = useTheme();\n\n return <div>theme: {theme}</div>;\n}\n","/**\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 React, {useState} from 'react';\n\nexport function Component() {\n const [count, setCount] = useState(0);\n\n return (\n <div>\n <p>You clicked {count} times</p>\n <button onClick={() => setCount(count + 1)}>Click me</button>\n </div>\n );\n}\n","/**\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 function Component() {\n const [count] = require('react').useState(0);\n\n return count;\n}\n","/**\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 * as React from 'react';\nimport {Fragment, useCallback, useState} from 'react';\n\nexport function ListItem({item, removeItem, toggleItem}) {\n const handleDelete = useCallback(() => {\n removeItem(item);\n }, [item, removeItem]);\n\n const handleToggle = useCallback(() => {\n toggleItem(item);\n }, [item, toggleItem]);\n\n return (\n <li>\n <button onClick={handleDelete}>Delete</button>\n <label>\n <input\n checked={item.isComplete}\n onChange={handleToggle}\n type=\"checkbox\"\n />{' '}\n {item.text}\n </label>\n </li>\n );\n}\n\nexport function List(props) {\n const [newItemText, setNewItemText] = useState('');\n const [items, setItems] = useState([\n {id: 1, isComplete: true, text: 'First'},\n {id: 2, isComplete: true, text: 'Second'},\n {id: 3, isComplete: false, text: 'Third'},\n ]);\n const [uid, setUID] = useState(4);\n\n const handleClick = useCallback(() => {\n if (newItemText !== '') {\n setItems([\n ...items,\n {\n id: uid,\n isComplete: false,\n text: newItemText,\n },\n ]);\n setUID(uid + 1);\n setNewItemText('');\n }\n }, [newItemText, items, uid]);\n\n const handleKeyPress = useCallback(\n event => {\n if (event.key === 'Enter') {\n handleClick();\n }\n },\n [handleClick],\n );\n\n const handleChange = useCallback(\n event => {\n setNewItemText(event.currentTarget.value);\n },\n [setNewItemText],\n );\n\n const removeItem = useCallback(\n itemToRemove => setItems(items.filter(item => item !== itemToRemove)),\n [items],\n );\n\n const toggleItem = useCallback(\n itemToToggle => {\n // Dont use indexOf()\n // because editing props in DevTools creates a new Object.\n const index = items.findIndex(item => item.id === itemToToggle.id);\n\n setItems(\n items\n .slice(0, index)\n .concat({\n ...itemToToggle,\n isComplete: !itemToToggle.isComplete,\n })\n .concat(items.slice(index + 1)),\n );\n },\n [items],\n );\n\n return (\n <Fragment>\n <h1>List</h1>\n <input\n type=\"text\"\n placeholder=\"New list item...\"\n value={newItemText}\n onChange={handleChange}\n onKeyPress={handleKeyPress}\n />\n <button disabled={newItemText === ''} onClick={handleClick}>\n <span role=\"img\" aria-label=\"Add item\">\n Add\n </span>\n </button>\n <ul>\n {items.map(item => (\n <ListItem\n key={item.id}\n item={item}\n removeItem={removeItem}\n toggleItem={toggleItem}\n />\n ))}\n </ul>\n </Fragment>\n );\n}\n"],"names":["Component","count","setCount","useState","isDarkMode","useIsDarkMode","useEffect","handleClick","React","useEffectCreate","ThemeContext","createContext","useTheme","theme","useContext","useDebugValue","require","ListItem","item","removeItem","toggleItem","handleDelete","useCallback","handleToggle","React.createElement","isComplete","text","List","props","newItemText","setNewItemText","items","setItems","id","uid","setUID","handleKeyPress","event","key","handleChange","currentTarget","value","itemToRemove","filter","itemToToggle","index","findIndex","slice","concat","Fragment","map"],"mappings":"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"}
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/ComponentWithCustomHook.js
+2 -3
@@ -33,10 +33,9 @@ function Component() {
33 }
34
35 function useIsDarkMode() {
36 - const [isDarkMode, setIsDarkMode] = (0, _react.useState)(0);
36 + const [isDarkMode] = (0, _react.useState)(false);
37 (0, _react.useEffect)(function useEffectCreate() {// Here is where we may listen to a "theme" event...
38 }, []);
39 return isDarkMode;
40 }
41 -//# sourceMappingURL=ComponentWithCustomHook.js.map
42 -//# sourceURL=ComponentWithCustomHook.js
\ No newline at end of file
41 +//# sourceMappingURL=ComponentWithCustomHook.js.map
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/ComponentWithCustomHook.js.map
+1 -1
@@ -1 +1 @@
1 -{"version":3,"sources":["ComponentWithCustomHook.js"],"names":["Component","count","setCount","isDarkMode","useIsDarkMode","handleClick","setIsDarkMode","useEffectCreate"],"mappings":";;;;;;;AASA;;;;;;AATA;;;;;;;;AAWO,SAASA,SAAT,GAAqB;AAC1B,QAAM,CAACC,KAAD,EAAQC,QAAR,IAAoB,qBAAS,CAAT,CAA1B;AACA,QAAMC,UAAU,GAAGC,aAAa,EAAhC;AAEA,wBAAU,MAAM,CACd;AACD,GAFD,EAEG,EAFH;;AAIA,QAAMC,WAAW,GAAG,MAAMH,QAAQ,CAACD,KAAK,GAAG,CAAT,CAAlC;;AAEA,sBACE,yEACE,yDAAiBE,UAAjB,CADF,eAEE,qDAAaF,KAAb,CAFF,eAGE;AAAQ,IAAA,OAAO,EAAEI;AAAjB,oBAHF,CADF;AAOD;;AAED,SAASD,aAAT,GAAyB;AACvB,QAAM,CAACD,UAAD,EAAaG,aAAb,IAA8B,qBAAS,CAAT,CAApC;AAEA,wBAAU,SAASC,eAAT,GAA2B,CACnC;AACD,GAFD,EAEG,EAFH;AAIA,SAAOJ,UAAP;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 React, {useEffect, useState} from 'react';\n\nexport function Component() {\n const [count, setCount] = useState(0);\n const isDarkMode = useIsDarkMode();\n\n useEffect(() => {\n // ...\n }, []);\n\n const handleClick = () => setCount(count + 1);\n\n return (\n <>\n <div>Dark mode? {isDarkMode}</div>\n <div>Count: {count}</div>\n <button onClick={handleClick}>Update count</button>\n </>\n );\n}\n\nfunction useIsDarkMode() {\n const [isDarkMode, setIsDarkMode] = useState(0);\n\n useEffect(function useEffectCreate() {\n // Here is where we may listen to a \"theme\" event...\n }, []);\n\n return isDarkMode;\n}"]}
\ No newline at end of file
1 +{"version":3,"sources":["ComponentWithCustomHook.js"],"names":["Component","count","setCount","isDarkMode","useIsDarkMode","handleClick","useEffectCreate"],"mappings":";;;;;;;AASA;;;;;;AATA;;;;;;;;AAWO,SAASA,SAAT,GAAqB;AAC1B,QAAM,CAACC,KAAD,EAAQC,QAAR,IAAoB,qBAAS,CAAT,CAA1B;AACA,QAAMC,UAAU,GAAGC,aAAa,EAAhC;AAEA,wBAAU,MAAM,CACd;AACD,GAFD,EAEG,EAFH;;AAIA,QAAMC,WAAW,GAAG,MAAMH,QAAQ,CAACD,KAAK,GAAG,CAAT,CAAlC;;AAEA,sBACE,yEACE,yDAAiBE,UAAjB,CADF,eAEE,qDAAaF,KAAb,CAFF,eAGE;AAAQ,IAAA,OAAO,EAAEI;AAAjB,oBAHF,CADF;AAOD;;AAED,SAASD,aAAT,GAAyB;AACvB,QAAM,CAACD,UAAD,IAAe,qBAAS,KAAT,CAArB;AAEA,wBAAU,SAASG,eAAT,GAA2B,CACnC;AACD,GAFD,EAEG,EAFH;AAIA,SAAOH,UAAP;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 React, {useEffect, useState} from 'react';\n\nexport function Component() {\n const [count, setCount] = useState(0);\n const isDarkMode = useIsDarkMode();\n\n useEffect(() => {\n // ...\n }, []);\n\n const handleClick = () => setCount(count + 1);\n\n return (\n <>\n <div>Dark mode? {isDarkMode}</div>\n <div>Count: {count}</div>\n <button onClick={handleClick}>Update count</button>\n </>\n );\n}\n\nfunction useIsDarkMode() {\n const [isDarkMode] = useState(false);\n\n useEffect(function useEffectCreate() {\n // Here is where we may listen to a \"theme\" event...\n }, []);\n\n return isDarkMode;\n}\n"]}
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/ComponentWithExternalCustomHooks.js
+1 -2
@@ -23,5 +23,4 @@ function Component() {
23 const theme = (0, _useTheme.default)();
24 return /*#__PURE__*/_react.default.createElement("div", null, "theme: ", theme);
25 }
26 -//# sourceMappingURL=ComponentWithExternalCustomHooks.js.map
27 -//# sourceURL=ComponentWithExternalCustomHooks.js
\ No newline at end of file
26 +//# sourceMappingURL=ComponentWithExternalCustomHooks.js.map
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/Example.js
+1 -2
@@ -25,5 +25,4 @@ function Component() {
25 onClick: () => setCount(count + 1)
26 }, "Click me"));
27 }
28 -//# sourceMappingURL=Example.js.map
29 -//# sourceURL=Example.js
\ No newline at end of file
28 +//# sourceMappingURL=Example.js.map
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/InlineRequire.js
+1 -2
@@ -18,5 +18,4 @@ function Component() {
18
19 return count;
20 }
21 -//# sourceMappingURL=InlineRequire.js.map
22 -//# sourceURL=InlineRequire.js
\ No newline at end of file
21 +//# sourceMappingURL=InlineRequire.js.map
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/ToDoList.js
+1 -2
@@ -103,5 +103,4 @@ function List(props) {
103 toggleItem: toggleItem
104 }))));
105 }
106 -//# sourceMappingURL=ToDoList.js.map
107 -//# sourceURL=ToDoList.js
\ No newline at end of file
106 +//# sourceMappingURL=ToDoList.js.map
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/index.js new
+57
@@ -0,0 +1,57 @@
1 +"use strict";
2 +
3 +Object.defineProperty(exports, "__esModule", {
4 + value: true
5 +});
6 +Object.defineProperty(exports, "ComponentWithCustomHook", {
7 + enumerable: true,
8 + get: function () {
9 + return _ComponentWithCustomHook.Component;
10 + }
11 +});
12 +Object.defineProperty(exports, "ComponentWithExternalCustomHooks", {
13 + enumerable: true,
14 + get: function () {
15 + return _ComponentWithExternalCustomHooks.Component;
16 + }
17 +});
18 +Object.defineProperty(exports, "Example", {
19 + enumerable: true,
20 + get: function () {
21 + return _Example.Component;
22 + }
23 +});
24 +Object.defineProperty(exports, "InlineRequire", {
25 + enumerable: true,
26 + get: function () {
27 + return _InlineRequire.Component;
28 + }
29 +});
30 +Object.defineProperty(exports, "useTheme", {
31 + enumerable: true,
32 + get: function () {
33 + return _useTheme.default;
34 + }
35 +});
36 +exports.ToDoList = void 0;
37 +
38 +var _ComponentWithCustomHook = require("./ComponentWithCustomHook");
39 +
40 +var _ComponentWithExternalCustomHooks = require("./ComponentWithExternalCustomHooks");
41 +
42 +var _Example = require("./Example");
43 +
44 +var _InlineRequire = require("./InlineRequire");
45 +
46 +var ToDoList = _interopRequireWildcard(require("./ToDoList"));
47 +
48 +exports.ToDoList = ToDoList;
49 +
50 +var _useTheme = _interopRequireDefault(require("./useTheme"));
51 +
52 +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
53 +
54 +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
55 +
56 +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; }
57 +//# sourceMappingURL=index.js.map
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/index.js.map new
+1
@@ -0,0 +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"]}
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/external/useTheme.js
+1 -2
@@ -24,5 +24,4 @@ function useTheme() {
24 (0, _react.useDebugValue)(theme);
25 return theme;
26 }
27 -//# sourceMappingURL=useTheme.js.map
28 -//# sourceURL=useTheme.js
\ No newline at end of file
27 +//# sourceMappingURL=useTheme.js.map
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/inline/ComponentWithCustomHook.js
+2 -3
@@ -33,10 +33,9 @@ function Component() {
33 }
34
35 function useIsDarkMode() {
36 - const [isDarkMode, setIsDarkMode] = (0, _react.useState)(0);
36 + const [isDarkMode] = (0, _react.useState)(false);
37 (0, _react.useEffect)(function useEffectCreate() {// Here is where we may listen to a "theme" event...
38 }, []);
39 return isDarkMode;
40 }
41 -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbXBvbmVudFdpdGhDdXN0b21Ib29rLmpzIl0sIm5hbWVzIjpbIkNvbXBvbmVudCIsImNvdW50Iiwic2V0Q291bnQiLCJpc0RhcmtNb2RlIiwidXNlSXNEYXJrTW9kZSIsImhhbmRsZUNsaWNrIiwic2V0SXNEYXJrTW9kZSIsInVzZUVmZmVjdENyZWF0ZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztBQVNBOzs7Ozs7QUFUQTs7Ozs7Ozs7QUFXTyxTQUFTQSxTQUFULEdBQXFCO0FBQzFCLFFBQU0sQ0FBQ0MsS0FBRCxFQUFRQyxRQUFSLElBQW9CLHFCQUFTLENBQVQsQ0FBMUI7QUFDQSxRQUFNQyxVQUFVLEdBQUdDLGFBQWEsRUFBaEM7QUFFQSx3QkFBVSxNQUFNLENBQ2Q7QUFDRCxHQUZELEVBRUcsRUFGSDs7QUFJQSxRQUFNQyxXQUFXLEdBQUcsTUFBTUgsUUFBUSxDQUFDRCxLQUFLLEdBQUcsQ0FBVCxDQUFsQzs7QUFFQSxzQkFDRSx5RUFDRSx5REFBaUJFLFVBQWpCLENBREYsZUFFRSxxREFBYUYsS0FBYixDQUZGLGVBR0U7QUFBUSxJQUFBLE9BQU8sRUFBRUk7QUFBakIsb0JBSEYsQ0FERjtBQU9EOztBQUVELFNBQVNELGFBQVQsR0FBeUI7QUFDdkIsUUFBTSxDQUFDRCxVQUFELEVBQWFHLGFBQWIsSUFBOEIscUJBQVMsQ0FBVCxDQUFwQztBQUVBLHdCQUFVLFNBQVNDLGVBQVQsR0FBMkIsQ0FDbkM7QUFDRCxHQUZELEVBRUcsRUFGSDtBQUlBLFNBQU9KLFVBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29weXJpZ2h0IChjKSBGYWNlYm9vaywgSW5jLiBhbmQgaXRzIGFmZmlsaWF0ZXMuXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlXG4gKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuXG4gKlxuICogQGZsb3dcbiAqL1xuXG5pbXBvcnQgUmVhY3QsIHt1c2VFZmZlY3QsIHVzZVN0YXRlfSBmcm9tICdyZWFjdCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBDb21wb25lbnQoKSB7XG4gIGNvbnN0IFtjb3VudCwgc2V0Q291bnRdID0gdXNlU3RhdGUoMCk7XG4gIGNvbnN0IGlzRGFya01vZGUgPSB1c2VJc0RhcmtNb2RlKCk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICAvLyAuLi5cbiAgfSwgW10pO1xuXG4gIGNvbnN0IGhhbmRsZUNsaWNrID0gKCkgPT4gc2V0Q291bnQoY291bnQgKyAxKTtcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8ZGl2PkRhcmsgbW9kZT8ge2lzRGFya01vZGV9PC9kaXY+XG4gICAgICA8ZGl2PkNvdW50OiB7Y291bnR9PC9kaXY+XG4gICAgICA8YnV0dG9uIG9uQ2xpY2s9e2hhbmRsZUNsaWNrfT5VcGRhdGUgY291bnQ8L2J1dHRvbj5cbiAgICA8Lz5cbiAgKTtcbn1cblxuZnVuY3Rpb24gdXNlSXNEYXJrTW9kZSgpIHtcbiAgY29uc3QgW2lzRGFya01vZGUsIHNldElzRGFya01vZGVdID0gdXNlU3RhdGUoMCk7XG5cbiAgdXNlRWZmZWN0KGZ1bmN0aW9uIHVzZUVmZmVjdENyZWF0ZSgpIHtcbiAgICAvLyBIZXJlIGlzIHdoZXJlIHdlIG1heSBsaXN0ZW4gdG8gYSBcInRoZW1lXCIgZXZlbnQuLi5cbiAgfSwgW10pO1xuXG4gIHJldHVybiBpc0RhcmtNb2RlO1xufSJdfQ==
42 -//# sourceURL=ComponentWithCustomHook.js
\ No newline at end of file
41 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbXBvbmVudFdpdGhDdXN0b21Ib29rLmpzIl0sIm5hbWVzIjpbIkNvbXBvbmVudCIsImNvdW50Iiwic2V0Q291bnQiLCJpc0RhcmtNb2RlIiwidXNlSXNEYXJrTW9kZSIsImhhbmRsZUNsaWNrIiwidXNlRWZmZWN0Q3JlYXRlIl0sIm1hcHBpbmdzIjoiOzs7Ozs7O0FBU0E7Ozs7OztBQVRBOzs7Ozs7OztBQVdPLFNBQVNBLFNBQVQsR0FBcUI7QUFDMUIsUUFBTSxDQUFDQyxLQUFELEVBQVFDLFFBQVIsSUFBb0IscUJBQVMsQ0FBVCxDQUExQjtBQUNBLFFBQU1DLFVBQVUsR0FBR0MsYUFBYSxFQUFoQztBQUVBLHdCQUFVLE1BQU0sQ0FDZDtBQUNELEdBRkQsRUFFRyxFQUZIOztBQUlBLFFBQU1DLFdBQVcsR0FBRyxNQUFNSCxRQUFRLENBQUNELEtBQUssR0FBRyxDQUFULENBQWxDOztBQUVBLHNCQUNFLHlFQUNFLHlEQUFpQkUsVUFBakIsQ0FERixlQUVFLHFEQUFhRixLQUFiLENBRkYsZUFHRTtBQUFRLElBQUEsT0FBTyxFQUFFSTtBQUFqQixvQkFIRixDQURGO0FBT0Q7O0FBRUQsU0FBU0QsYUFBVCxHQUF5QjtBQUN2QixRQUFNLENBQUNELFVBQUQsSUFBZSxxQkFBUyxLQUFULENBQXJCO0FBRUEsd0JBQVUsU0FBU0csZUFBVCxHQUEyQixDQUNuQztBQUNELEdBRkQsRUFFRyxFQUZIO0FBSUEsU0FBT0gsVUFBUDtBQUNEIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmltcG9ydCBSZWFjdCwge3VzZUVmZmVjdCwgdXNlU3RhdGV9IGZyb20gJ3JlYWN0JztcblxuZXhwb3J0IGZ1bmN0aW9uIENvbXBvbmVudCgpIHtcbiAgY29uc3QgW2NvdW50LCBzZXRDb3VudF0gPSB1c2VTdGF0ZSgwKTtcbiAgY29uc3QgaXNEYXJrTW9kZSA9IHVzZUlzRGFya01vZGUoKTtcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIC8vIC4uLlxuICB9LCBbXSk7XG5cbiAgY29uc3QgaGFuZGxlQ2xpY2sgPSAoKSA9PiBzZXRDb3VudChjb3VudCArIDEpO1xuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxkaXY+RGFyayBtb2RlPyB7aXNEYXJrTW9kZX08L2Rpdj5cbiAgICAgIDxkaXY+Q291bnQ6IHtjb3VudH08L2Rpdj5cbiAgICAgIDxidXR0b24gb25DbGljaz17aGFuZGxlQ2xpY2t9PlVwZGF0ZSBjb3VudDwvYnV0dG9uPlxuICAgIDwvPlxuICApO1xufVxuXG5mdW5jdGlvbiB1c2VJc0RhcmtNb2RlKCkge1xuICBjb25zdCBbaXNEYXJrTW9kZV0gPSB1c2VTdGF0ZShmYWxzZSk7XG5cbiAgdXNlRWZmZWN0KGZ1bmN0aW9uIHVzZUVmZmVjdENyZWF0ZSgpIHtcbiAgICAvLyBIZXJlIGlzIHdoZXJlIHdlIG1heSBsaXN0ZW4gdG8gYSBcInRoZW1lXCIgZXZlbnQuLi5cbiAgfSwgW10pO1xuXG4gIHJldHVybiBpc0RhcmtNb2RlO1xufVxuIl19
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/inline/ComponentWithExternalCustomHooks.js
+1 -2
@@ -23,5 +23,4 @@ function Component() {
23 const theme = (0, _useTheme.default)();
24 return /*#__PURE__*/_react.default.createElement("div", null, "theme: ", theme);
25 }
26 -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzLmpzIl0sIm5hbWVzIjpbIkNvbXBvbmVudCIsInRoZW1lIl0sIm1hcHBpbmdzIjoiOzs7Ozs7O0FBU0E7O0FBQ0E7Ozs7QUFWQTs7Ozs7Ozs7QUFZTyxTQUFTQSxTQUFULEdBQXFCO0FBQzFCLFFBQU1DLEtBQUssR0FBRyx3QkFBZDtBQUVBLHNCQUFPLHFEQUFhQSxLQUFiLENBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29weXJpZ2h0IChjKSBGYWNlYm9vaywgSW5jLiBhbmQgaXRzIGFmZmlsaWF0ZXMuXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlXG4gKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuXG4gKlxuICogQGZsb3dcbiAqL1xuXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnO1xuaW1wb3J0IHVzZVRoZW1lIGZyb20gJy4vdXNlVGhlbWUnO1xuXG5leHBvcnQgZnVuY3Rpb24gQ29tcG9uZW50KCkge1xuICBjb25zdCB0aGVtZSA9IHVzZVRoZW1lKCk7XG5cbiAgcmV0dXJuIDxkaXY+dGhlbWU6IHt0aGVtZX08L2Rpdj47XG59XG4iXX0=
27 -//# sourceURL=ComponentWithExternalCustomHooks.js
\ No newline at end of file
26 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkNvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzLmpzIl0sIm5hbWVzIjpbIkNvbXBvbmVudCIsInRoZW1lIl0sIm1hcHBpbmdzIjoiOzs7Ozs7O0FBU0E7O0FBQ0E7Ozs7QUFWQTs7Ozs7Ozs7QUFZTyxTQUFTQSxTQUFULEdBQXFCO0FBQzFCLFFBQU1DLEtBQUssR0FBRyx3QkFBZDtBQUVBLHNCQUFPLHFEQUFhQSxLQUFiLENBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29weXJpZ2h0IChjKSBGYWNlYm9vaywgSW5jLiBhbmQgaXRzIGFmZmlsaWF0ZXMuXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlXG4gKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuXG4gKlxuICogQGZsb3dcbiAqL1xuXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnO1xuaW1wb3J0IHVzZVRoZW1lIGZyb20gJy4vdXNlVGhlbWUnO1xuXG5leHBvcnQgZnVuY3Rpb24gQ29tcG9uZW50KCkge1xuICBjb25zdCB0aGVtZSA9IHVzZVRoZW1lKCk7XG5cbiAgcmV0dXJuIDxkaXY+dGhlbWU6IHt0aGVtZX08L2Rpdj47XG59XG4iXX0=
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/inline/Example.js
+1 -2
@@ -25,5 +25,4 @@ function Component() {
25 onClick: () => setCount(count + 1)
26 }, "Click me"));
27 }
28 -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkV4YW1wbGUuanMiXSwibmFtZXMiOlsiQ29tcG9uZW50IiwiY291bnQiLCJzZXRDb3VudCJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztBQVNBOzs7Ozs7QUFUQTs7Ozs7Ozs7QUFXTyxTQUFTQSxTQUFULEdBQXFCO0FBQzFCLFFBQU0sQ0FBQ0MsS0FBRCxFQUFRQyxRQUFSLElBQW9CLHFCQUFTLENBQVQsQ0FBMUI7QUFFQSxzQkFDRSx1REFDRSx3REFBZ0JELEtBQWhCLFdBREYsZUFFRTtBQUFRLElBQUEsT0FBTyxFQUFFLE1BQU1DLFFBQVEsQ0FBQ0QsS0FBSyxHQUFHLENBQVQ7QUFBL0IsZ0JBRkYsQ0FERjtBQU1EIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmltcG9ydCBSZWFjdCwge3VzZVN0YXRlfSBmcm9tICdyZWFjdCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBDb21wb25lbnQoKSB7XG4gIGNvbnN0IFtjb3VudCwgc2V0Q291bnRdID0gdXNlU3RhdGUoMCk7XG5cbiAgcmV0dXJuIChcbiAgICA8ZGl2PlxuICAgICAgPHA+WW91IGNsaWNrZWQge2NvdW50fSB0aW1lczwvcD5cbiAgICAgIDxidXR0b24gb25DbGljaz17KCkgPT4gc2V0Q291bnQoY291bnQgKyAxKX0+Q2xpY2sgbWU8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJdfQ==
29 -//# sourceURL=Example.js
\ No newline at end of file
28 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkV4YW1wbGUuanMiXSwibmFtZXMiOlsiQ29tcG9uZW50IiwiY291bnQiLCJzZXRDb3VudCJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztBQVNBOzs7Ozs7QUFUQTs7Ozs7Ozs7QUFXTyxTQUFTQSxTQUFULEdBQXFCO0FBQzFCLFFBQU0sQ0FBQ0MsS0FBRCxFQUFRQyxRQUFSLElBQW9CLHFCQUFTLENBQVQsQ0FBMUI7QUFFQSxzQkFDRSx1REFDRSx3REFBZ0JELEtBQWhCLFdBREYsZUFFRTtBQUFRLElBQUEsT0FBTyxFQUFFLE1BQU1DLFFBQVEsQ0FBQ0QsS0FBSyxHQUFHLENBQVQ7QUFBL0IsZ0JBRkYsQ0FERjtBQU1EIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmltcG9ydCBSZWFjdCwge3VzZVN0YXRlfSBmcm9tICdyZWFjdCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBDb21wb25lbnQoKSB7XG4gIGNvbnN0IFtjb3VudCwgc2V0Q291bnRdID0gdXNlU3RhdGUoMCk7XG5cbiAgcmV0dXJuIChcbiAgICA8ZGl2PlxuICAgICAgPHA+WW91IGNsaWNrZWQge2NvdW50fSB0aW1lczwvcD5cbiAgICAgIDxidXR0b24gb25DbGljaz17KCkgPT4gc2V0Q291bnQoY291bnQgKyAxKX0+Q2xpY2sgbWU8L2J1dHRvbj5cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJdfQ==
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/inline/InlineRequire.js
+1 -2
@@ -18,5 +18,4 @@ function Component() {
18
19 return count;
20 }
21 -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIklubGluZVJlcXVpcmUuanMiXSwibmFtZXMiOlsiQ29tcG9uZW50IiwiY291bnQiLCJyZXF1aXJlIiwidXNlU3RhdGUiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7QUFBQTs7Ozs7Ozs7QUFTTyxTQUFTQSxTQUFULEdBQXFCO0FBQzFCLFFBQU0sQ0FBQ0MsS0FBRCxJQUFVQyxPQUFPLENBQUMsT0FBRCxDQUFQLENBQWlCQyxRQUFqQixDQUEwQixDQUExQixDQUFoQjs7QUFFQSxTQUFPRixLQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAoYykgRmFjZWJvb2ssIEluYy4gYW5kIGl0cyBhZmZpbGlhdGVzLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLlxuICpcbiAqIEBmbG93XG4gKi9cblxuZXhwb3J0IGZ1bmN0aW9uIENvbXBvbmVudCgpIHtcbiAgY29uc3QgW2NvdW50XSA9IHJlcXVpcmUoJ3JlYWN0JykudXNlU3RhdGUoMCk7XG5cbiAgcmV0dXJuIGNvdW50O1xufVxuIl19
22 -//# sourceURL=InlineRequire.js
\ No newline at end of file
21 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIklubGluZVJlcXVpcmUuanMiXSwibmFtZXMiOlsiQ29tcG9uZW50IiwiY291bnQiLCJyZXF1aXJlIiwidXNlU3RhdGUiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7QUFBQTs7Ozs7Ozs7QUFTTyxTQUFTQSxTQUFULEdBQXFCO0FBQzFCLFFBQU0sQ0FBQ0MsS0FBRCxJQUFVQyxPQUFPLENBQUMsT0FBRCxDQUFQLENBQWlCQyxRQUFqQixDQUEwQixDQUExQixDQUFoQjs7QUFFQSxTQUFPRixLQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAoYykgRmFjZWJvb2ssIEluYy4gYW5kIGl0cyBhZmZpbGlhdGVzLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLlxuICpcbiAqIEBmbG93XG4gKi9cblxuZXhwb3J0IGZ1bmN0aW9uIENvbXBvbmVudCgpIHtcbiAgY29uc3QgW2NvdW50XSA9IHJlcXVpcmUoJ3JlYWN0JykudXNlU3RhdGUoMCk7XG5cbiAgcmV0dXJuIGNvdW50O1xufVxuIl19
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/inline/ToDoList.js
+1 -2
@@ -103,5 +103,4 @@ function List(props) {
103 toggleItem: toggleItem
104 }))));
105 }
106 -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIlRvRG9MaXN0LmpzIl0sIm5hbWVzIjpbIkxpc3RJdGVtIiwiaXRlbSIsInJlbW92ZUl0ZW0iLCJ0b2dnbGVJdGVtIiwiaGFuZGxlRGVsZXRlIiwiaGFuZGxlVG9nZ2xlIiwiaXNDb21wbGV0ZSIsInRleHQiLCJMaXN0IiwicHJvcHMiLCJuZXdJdGVtVGV4dCIsInNldE5ld0l0ZW1UZXh0IiwiaXRlbXMiLCJzZXRJdGVtcyIsImlkIiwidWlkIiwic2V0VUlEIiwiaGFuZGxlQ2xpY2siLCJoYW5kbGVLZXlQcmVzcyIsImV2ZW50Iiwia2V5IiwiaGFuZGxlQ2hhbmdlIiwiY3VycmVudFRhcmdldCIsInZhbHVlIiwiaXRlbVRvUmVtb3ZlIiwiZmlsdGVyIiwiaXRlbVRvVG9nZ2xlIiwiaW5kZXgiLCJmaW5kSW5kZXgiLCJzbGljZSIsImNvbmNhdCIsIm1hcCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFTQTs7Ozs7O0FBVEE7Ozs7Ozs7O0FBWU8sU0FBU0EsUUFBVCxDQUFrQjtBQUFDQyxFQUFBQSxJQUFEO0FBQU9DLEVBQUFBLFVBQVA7QUFBbUJDLEVBQUFBO0FBQW5CLENBQWxCLEVBQWtEO0FBQ3ZELFFBQU1DLFlBQVksR0FBRyx1QkFBWSxNQUFNO0FBQ3JDRixJQUFBQSxVQUFVLENBQUNELElBQUQsQ0FBVjtBQUNELEdBRm9CLEVBRWxCLENBQUNBLElBQUQsRUFBT0MsVUFBUCxDQUZrQixDQUFyQjtBQUlBLFFBQU1HLFlBQVksR0FBRyx1QkFBWSxNQUFNO0FBQ3JDRixJQUFBQSxVQUFVLENBQUNGLElBQUQsQ0FBVjtBQUNELEdBRm9CLEVBRWxCLENBQUNBLElBQUQsRUFBT0UsVUFBUCxDQUZrQixDQUFyQjtBQUlBLHNCQUNFLDZDQUNFO0FBQVEsSUFBQSxPQUFPLEVBQUVDO0FBQWpCLGNBREYsZUFFRSxnREFDRTtBQUNFLElBQUEsT0FBTyxFQUFFSCxJQUFJLENBQUNLLFVBRGhCO0FBRUUsSUFBQSxRQUFRLEVBQUVELFlBRlo7QUFHRSxJQUFBLElBQUksRUFB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UDtBQUVFLElBQUEsV0FBVyxFQUFDLGtCQUZkO0FBR0UsSUFBQSxLQUFLLEVBQUVGLFdBSFQ7QUFJRSxJQUFBLFFBQVEsRUFBRVcsWUFKWjtBQUtFLElBQUEsVUFBVSxFQUFFSDtBQUxkLElBRkYsZUFTRTtBQUFRLElBQUEsUUFBUSxFQUFFUixXQUFXLEtBQUssRUFBbEM7QUFBc0MsSUFBQSxPQUFPLEVBQUVPO0FBQS9DLGtCQUNFO0FBQU0sSUFBQSxJQUFJLEVBQUMsS0FBWDtBQUFpQixrQkFBVztBQUE1QixXQURGLENBVEYsZUFjRSxnQ0FDR0wsS0FBSyxDQUFDbUIsR0FBTixDQUFVOUIsSUFBSSxpQkFDYixvQkFBQyxRQUFEO0FBQ0UsSUFBQSxHQUFHLEVBQUVBLElBQUksQ0FBQ2EsRUFEWjtBQUVFLElBQUEsSUFBSSxFQUFFYixJQUZSO0FBR0UsSUFBQSxVQUFVLEVBQUVDLFVBSGQ7QUFJRSxJQUFBLFVBQVUsRUFBRUM7QUFKZCxJQURELENBREgsQ0FkRixDQURGO0FBMkJEIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0JztcbmltcG9ydCB7RnJhZ21lbnQsIHVzZUNhbGxiYWNrLCB1c2VTdGF0ZX0gZnJvbSAncmVhY3QnO1xuXG5leHBvcnQgZnVuY3Rpb24gTGlzdEl0ZW0oe2l0ZW0sIHJlbW92ZUl0ZW0sIHRvZ2dsZUl0ZW19KSB7XG4gIGNvbnN0IGhhbmRsZURlbGV0ZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICByZW1vdmVJdGVtKGl0ZW0pO1xuICB9LCBbaXRlbSwgcmVtb3ZlSXRlbV0pO1xuXG4gIGNvbnN0IGhhbmRsZVRvZ2dsZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICB0b2dnbGVJdGVtKGl0ZW0pO1xuICB9LCBbaXRlbSwgdG9nZ2xlSXRlbV0pO1xuXG4gIHJldHVybiAoXG4gICAgPGxpPlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXtoYW5kbGVEZWxldGV9PkRlbGV0ZTwvYnV0dG9uPlxuICAgICAgPGxhYmVsPlxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICBjaGVja2VkPXtpdGVtLmlzQ29tcGxldGV9XG4gICAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVRvZ2dsZX1cbiAgICAgICAgICB0eXBlPVwiY2hlY2tib3hcIlxuICAgICAgICAvPnsnICd9XG4gICAgICAgIHtpdGVtLnRleHR9XG4gICAgICA8L2xhYmVsPlxuICAgIDwvbGk+XG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBMaXN0KHByb3BzKSB7XG4gIGNvbnN0IFtuZXdJdGVtVGV4dCwgc2V0TmV3SXRlbVRleHRdID0gdXNlU3RhdGUoJycpO1xuICBjb25zdCBbaXRlbXMsIHNldEl0ZW1zXSA9IHVzZVN0YXRlKFtcbiAgICB7aWQ6IDEsIGlzQ29tcGxldGU6IHRydWUsIHRleHQ6ICdGaXJzdCd9LFxuICAgIHtpZDogMiwgaXNDb21wbGV0ZTogdHJ1ZSwgdGV4dDogJ1NlY29uZCd9LFxuICAgIHtpZDogMywgaXNDb21wbGV0ZTogZmFsc2UsIHRleHQ6ICdUaGlyZCd9LFxuICBdKTtcbiAgY29uc3QgW3VpZCwgc2V0VUlEXSA9IHVzZVN0YXRlKDQpO1xuXG4gIGNvbnN0IGhhbmRsZUNsaWNrID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChuZXdJdGVtVGV4dCAhPT0gJycpIHtcbiAgICAgIHNldEl0ZW1zKFtcbiAgICAgICAgLi4uaXRlbXMsXG4gICAgICAgIHtcbiAgICAgICAgICBpZDogdWlkLFxuICAgICAgICAgIGlzQ29tcGxldGU6IGZhbHNlLFxuICAgICAgICAgIHRleHQ6IG5ld0l0ZW1UZXh0LFxuICAgICAgICB9LFxuICAgICAgXSk7XG4gICAgICBzZXRVSUQodWlkICsgMSk7XG4gICAgICBzZXROZXdJdGVtVGV4dCgnJyk7XG4gICAgfVxuICB9LCBbbmV3SXRlbVRleHQsIGl0ZW1zLCB1aWRdKTtcblxuICBjb25zdCBoYW5kbGVLZXlQcmVzcyA9IHVzZUNhbGxiYWNrKFxuICAgIGV2ZW50ID0+IHtcbiAgICAgIGlmIChldmVudC5rZXkgPT09ICdFbnRlcicpIHtcbiAgICAgICAgaGFuZGxlQ2xpY2soKTtcbiAgICAgIH1cbiAgICB9LFxuICAgIFtoYW5kbGVDbGlja10sXG4gICk7XG5cbiAgY29uc3QgaGFuZGxlQ2hhbmdlID0gdXNlQ2FsbGJhY2soXG4gICAgZXZlbnQgPT4ge1xuICAgICAgc2V0TmV3SXRlbVRleHQoZXZlbnQuY3VycmVudFRhcmdldC52YWx1ZSk7XG4gICAgfSxcbiAgICBbc2V0TmV3SXRlbVRleHRdLFxuICApO1xuXG4gIGNvbnN0IHJlbW92ZUl0ZW0gPSB1c2VDYWxsYmFjayhcbiAgICBpdGVtVG9SZW1vdmUgPT4gc2V0SXRlbXMoaXRlbXMuZmlsdGVyKGl0ZW0gPT4gaXRlbSAhPT0gaXRlbVRvUmVtb3ZlKSksXG4gICAgW2l0ZW1zXSxcbiAgKTtcblxuICBjb25zdCB0b2dnbGVJdGVtID0gdXNlQ2FsbGJhY2soXG4gICAgaXRlbVRvVG9nZ2xlID0+IHtcbiAgICAgIC8vIERvbnQgdXNlIGluZGV4T2YoKVxuICAgICAgLy8gYmVjYXVzZSBlZGl0aW5nIHByb3BzIGluIERldlRvb2xzIGNyZWF0ZXMgYSBuZXcgT2JqZWN0LlxuICAgICAgY29uc3QgaW5kZXggPSBpdGVtcy5maW5kSW5kZXgoaXRlbSA9PiBpdGVtLmlkID09PSBpdGVtVG9Ub2dnbGUuaWQpO1xuXG4gICAgICBzZXRJdGVtcyhcbiAgICAgICAgaXRlbXNcbiAgICAgICAgICAuc2xpY2UoMCwgaW5kZXgpXG4gICAgICAgICAgLmNvbmNhdCh7XG4gICAgICAgICAgICAuLi5pdGVtVG9Ub2dnbGUsXG4gICAgICAgICAgICBpc0NvbXBsZXRlOiAhaXRlbVRvVG9nZ2xlLmlzQ29tcGxldGUsXG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY29uY2F0KGl0ZW1zLnNsaWNlKGluZGV4ICsgMSkpLFxuICAgICAgKTtcbiAgICB9LFxuICAgIFtpdGVtc10sXG4gICk7XG5cbiAgcmV0dXJuIChcbiAgICA8RnJhZ21lbnQ+XG4gICAgICA8aDE+TGlzdDwvaDE+XG4gICAgICA8aW5wdXRcbiAgICAgICAgdHlwZT1cInRleHRcIlxuICAgICAgICBwbGFjZWhvbGRlcj1cIk5ldyBsaXN0IGl0ZW0uLi5cIlxuICAgICAgICB2YWx1ZT17bmV3SXRlbVRleHR9XG4gICAgICAgIG9uQ2hhbmdlPXtoYW5kbGVDaGFuZ2V9XG4gICAgICAgIG9uS2V5UHJlc3M9e2hhbmRsZUtleVByZXNzfVxuICAgICAgLz5cbiAgICAgIDxidXR0b24gZGlzYWJsZWQ9e25ld0l0ZW1UZXh0ID09PSAnJ30gb25DbGljaz17aGFuZGxlQ2xpY2t9PlxuICAgICAgICA8c3BhbiByb2xlPVwiaW1nXCIgYXJpYS1sYWJlbD1cIkFkZCBpdGVtXCI+XG4gICAgICAgICAgQWRkXG4gICAgICAgIDwvc3Bhbj5cbiAgICAgIDwvYnV0dG9uPlxuICAgICAgPHVsPlxuICAgICAgICB7aXRlbXMubWFwKGl0ZW0gPT4gKFxuICAgICAgICAgIDxMaXN0SXRlbVxuICAgICAgICAgICAga2V5PXtpdGVtLmlkfVxuICAgICAgICAgICAgaXRlbT17aXRlbX1cbiAgICAgICAgICAgIHJlbW92ZUl0ZW09e3JlbW92ZUl0ZW19XG4gICAgICAgICAgICB0b2dnbGVJdGVtPXt0b2dnbGVJdGVtfVxuICAgICAgICAgIC8+XG4gICAgICAgICkpfVxuICAgICAgPC91bD5cbiAgICA8L0ZyYWdtZW50PlxuICApO1xufVxuIl19
107 -//# sourceURL=ToDoList.js
\ No newline at end of file
106 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIlRvRG9MaXN0LmpzIl0sIm5hbWVzIjpbIkxpc3RJdGVtIiwiaXRlbSIsInJlbW92ZUl0ZW0iLCJ0b2dnbGVJdGVtIiwiaGFuZGxlRGVsZXRlIiwiaGFuZGxlVG9nZ2xlIiwiaXNDb21wbGV0ZSIsInRleHQiLCJMaXN0IiwicHJvcHMiLCJuZXdJdGVtVGV4dCIsInNldE5ld0l0ZW1UZXh0IiwiaXRlbXMiLCJzZXRJdGVtcyIsImlkIiwidWlkIiwic2V0VUlEIiwiaGFuZGxlQ2xpY2siLCJoYW5kbGVLZXlQcmVzcyIsImV2ZW50Iiwia2V5IiwiaGFuZGxlQ2hhbmdlIiwiY3VycmVudFRhcmdldCIsInZhbHVlIiwiaXRlbVRvUmVtb3ZlIiwiZmlsdGVyIiwiaXRlbVRvVG9nZ2xlIiwiaW5kZXgiLCJmaW5kSW5kZXgiLCJzbGljZSIsImNvbmNhdCIsIm1hcCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFTQTs7Ozs7O0FBVEE7Ozs7Ozs7O0FBWU8sU0FBU0EsUUFBVCxDQUFrQjtBQUFDQyxFQUFBQSxJQUFEO0FBQU9DLEVBQUFBLFVBQVA7QUFBbUJDLEVBQUFBO0FBQW5CLENBQWxCLEVBQWtEO0FBQ3ZELFFBQU1DLFlBQVksR0FBRyx1QkFBWSxNQUFNO0FBQ3JDRixJQUFBQSxVQUFVLENBQUNELElBQUQsQ0FBVjtBQUNELEdBRm9CLEVBRWxCLENBQUNBLElBQUQsRUFBT0MsVUFBUCxDQUZrQixDQUFyQjtBQUlBLFFBQU1HLFlBQVksR0FBRyx1QkFBWSxNQUFNO0FBQ3JDRixJQUFBQSxVQUFVLENBQUNGLElBQUQsQ0FBVjtBQUNELEdBRm9CLEVBRWxCLENBQUNBLElBQUQsRUFBT0UsVUFBUCxDQUZrQixDQUFyQjtBQUlBLHNCQUNFLDZDQUNFO0FBQVEsSUFBQSxPQUFPLEVBQUVDO0FBQWpCLGNBREYsZUFFRSxnREFDRTtBQUNFLElBQUEsT0FBTyxFQUFFSCxJQUFJLENBQUNLLFVBRGhCO0FBRUUsSUFBQSxRQUFRLEVBQUVELFlBRlo7QUFHRSxJQUFBLElBQUksRUFB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UDtBQUVFLElBQUEsV0FBVyxFQUFDLGtCQUZkO0FBR0UsSUFBQSxLQUFLLEVBQUVGLFdBSFQ7QUFJRSxJQUFBLFFBQVEsRUFBRVcsWUFKWjtBQUtFLElBQUEsVUFBVSxFQUFFSDtBQUxkLElBRkYsZUFTRTtBQUFRLElBQUEsUUFBUSxFQUFFUixXQUFXLEtBQUssRUFBbEM7QUFBc0MsSUFBQSxPQUFPLEVBQUVPO0FBQS9DLGtCQUNFO0FBQU0sSUFBQSxJQUFJLEVBQUMsS0FBWDtBQUFpQixrQkFBVztBQUE1QixXQURGLENBVEYsZUFjRSxnQ0FDR0wsS0FBSyxDQUFDbUIsR0FBTixDQUFVOUIsSUFBSSxpQkFDYixvQkFBQyxRQUFEO0FBQ0UsSUFBQSxHQUFHLEVBQUVBLElBQUksQ0FBQ2EsRUFEWjtBQUVFLElBQUEsSUFBSSxFQUFFYixJQUZSO0FBR0UsSUFBQSxVQUFVLEVBQUVDLFVBSGQ7QUFJRSxJQUFBLFVBQVUsRUFBRUM7QUFKZCxJQURELENBREgsQ0FkRixDQURGO0FBMkJEIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0JztcbmltcG9ydCB7RnJhZ21lbnQsIHVzZUNhbGxiYWNrLCB1c2VTdGF0ZX0gZnJvbSAncmVhY3QnO1xuXG5leHBvcnQgZnVuY3Rpb24gTGlzdEl0ZW0oe2l0ZW0sIHJlbW92ZUl0ZW0sIHRvZ2dsZUl0ZW19KSB7XG4gIGNvbnN0IGhhbmRsZURlbGV0ZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICByZW1vdmVJdGVtKGl0ZW0pO1xuICB9LCBbaXRlbSwgcmVtb3ZlSXRlbV0pO1xuXG4gIGNvbnN0IGhhbmRsZVRvZ2dsZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICB0b2dnbGVJdGVtKGl0ZW0pO1xuICB9LCBbaXRlbSwgdG9nZ2xlSXRlbV0pO1xuXG4gIHJldHVybiAoXG4gICAgPGxpPlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXtoYW5kbGVEZWxldGV9PkRlbGV0ZTwvYnV0dG9uPlxuICAgICAgPGxhYmVsPlxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICBjaGVja2VkPXtpdGVtLmlzQ29tcGxldGV9XG4gICAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVRvZ2dsZX1cbiAgICAgICAgICB0eXBlPVwiY2hlY2tib3hcIlxuICAgICAgICAvPnsnICd9XG4gICAgICAgIHtpdGVtLnRleHR9XG4gICAgICA8L2xhYmVsPlxuICAgIDwvbGk+XG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBMaXN0KHByb3BzKSB7XG4gIGNvbnN0IFtuZXdJdGVtVGV4dCwgc2V0TmV3SXRlbVRleHRdID0gdXNlU3RhdGUoJycpO1xuICBjb25zdCBbaXRlbXMsIHNldEl0ZW1zXSA9IHVzZVN0YXRlKFtcbiAgICB7aWQ6IDEsIGlzQ29tcGxldGU6IHRydWUsIHRleHQ6ICdGaXJzdCd9LFxuICAgIHtpZDogMiwgaXNDb21wbGV0ZTogdHJ1ZSwgdGV4dDogJ1NlY29uZCd9LFxuICAgIHtpZDogMywgaXNDb21wbGV0ZTogZmFsc2UsIHRleHQ6ICdUaGlyZCd9LFxuICBdKTtcbiAgY29uc3QgW3VpZCwgc2V0VUlEXSA9IHVzZVN0YXRlKDQpO1xuXG4gIGNvbnN0IGhhbmRsZUNsaWNrID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChuZXdJdGVtVGV4dCAhPT0gJycpIHtcbiAgICAgIHNldEl0ZW1zKFtcbiAgICAgICAgLi4uaXRlbXMsXG4gICAgICAgIHtcbiAgICAgICAgICBpZDogdWlkLFxuICAgICAgICAgIGlzQ29tcGxldGU6IGZhbHNlLFxuICAgICAgICAgIHRleHQ6IG5ld0l0ZW1UZXh0LFxuICAgICAgICB9LFxuICAgICAgXSk7XG4gICAgICBzZXRVSUQodWlkICsgMSk7XG4gICAgICBzZXROZXdJdGVtVGV4dCgnJyk7XG4gICAgfVxuICB9LCBbbmV3SXRlbVRleHQsIGl0ZW1zLCB1aWRdKTtcblxuICBjb25zdCBoYW5kbGVLZXlQcmVzcyA9IHVzZUNhbGxiYWNrKFxuICAgIGV2ZW50ID0+IHtcbiAgICAgIGlmIChldmVudC5rZXkgPT09ICdFbnRlcicpIHtcbiAgICAgICAgaGFuZGxlQ2xpY2soKTtcbiAgICAgIH1cbiAgICB9LFxuICAgIFtoYW5kbGVDbGlja10sXG4gICk7XG5cbiAgY29uc3QgaGFuZGxlQ2hhbmdlID0gdXNlQ2FsbGJhY2soXG4gICAgZXZlbnQgPT4ge1xuICAgICAgc2V0TmV3SXRlbVRleHQoZXZlbnQuY3VycmVudFRhcmdldC52YWx1ZSk7XG4gICAgfSxcbiAgICBbc2V0TmV3SXRlbVRleHRdLFxuICApO1xuXG4gIGNvbnN0IHJlbW92ZUl0ZW0gPSB1c2VDYWxsYmFjayhcbiAgICBpdGVtVG9SZW1vdmUgPT4gc2V0SXRlbXMoaXRlbXMuZmlsdGVyKGl0ZW0gPT4gaXRlbSAhPT0gaXRlbVRvUmVtb3ZlKSksXG4gICAgW2l0ZW1zXSxcbiAgKTtcblxuICBjb25zdCB0b2dnbGVJdGVtID0gdXNlQ2FsbGJhY2soXG4gICAgaXRlbVRvVG9nZ2xlID0+IHtcbiAgICAgIC8vIERvbnQgdXNlIGluZGV4T2YoKVxuICAgICAgLy8gYmVjYXVzZSBlZGl0aW5nIHByb3BzIGluIERldlRvb2xzIGNyZWF0ZXMgYSBuZXcgT2JqZWN0LlxuICAgICAgY29uc3QgaW5kZXggPSBpdGVtcy5maW5kSW5kZXgoaXRlbSA9PiBpdGVtLmlkID09PSBpdGVtVG9Ub2dnbGUuaWQpO1xuXG4gICAgICBzZXRJdGVtcyhcbiAgICAgICAgaXRlbXNcbiAgICAgICAgICAuc2xpY2UoMCwgaW5kZXgpXG4gICAgICAgICAgLmNvbmNhdCh7XG4gICAgICAgICAgICAuLi5pdGVtVG9Ub2dnbGUsXG4gICAgICAgICAgICBpc0NvbXBsZXRlOiAhaXRlbVRvVG9nZ2xlLmlzQ29tcGxldGUsXG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY29uY2F0KGl0ZW1zLnNsaWNlKGluZGV4ICsgMSkpLFxuICAgICAgKTtcbiAgICB9LFxuICAgIFtpdGVtc10sXG4gICk7XG5cbiAgcmV0dXJuIChcbiAgICA8RnJhZ21lbnQ+XG4gICAgICA8aDE+TGlzdDwvaDE+XG4gICAgICA8aW5wdXRcbiAgICAgICAgdHlwZT1cInRleHRcIlxuICAgICAgICBwbGFjZWhvbGRlcj1cIk5ldyBsaXN0IGl0ZW0uLi5cIlxuICAgICAgICB2YWx1ZT17bmV3SXRlbVRleHR9XG4gICAgICAgIG9uQ2hhbmdlPXtoYW5kbGVDaGFuZ2V9XG4gICAgICAgIG9uS2V5UHJlc3M9e2hhbmRsZUtleVByZXNzfVxuICAgICAgLz5cbiAgICAgIDxidXR0b24gZGlzYWJsZWQ9e25ld0l0ZW1UZXh0ID09PSAnJ30gb25DbGljaz17aGFuZGxlQ2xpY2t9PlxuICAgICAgICA8c3BhbiByb2xlPVwiaW1nXCIgYXJpYS1sYWJlbD1cIkFkZCBpdGVtXCI+XG4gICAgICAgICAgQWRkXG4gICAgICAgIDwvc3Bhbj5cbiAgICAgIDwvYnV0dG9uPlxuICAgICAgPHVsPlxuICAgICAgICB7aXRlbXMubWFwKGl0ZW0gPT4gKFxuICAgICAgICAgIDxMaXN0SXRlbVxuICAgICAgICAgICAga2V5PXtpdGVtLmlkfVxuICAgICAgICAgICAgaXRlbT17aXRlbX1cbiAgICAgICAgICAgIHJlbW92ZUl0ZW09e3JlbW92ZUl0ZW19XG4gICAgICAgICAgICB0b2dnbGVJdGVtPXt0b2dnbGVJdGVtfVxuICAgICAgICAgIC8+XG4gICAgICAgICkpfVxuICAgICAgPC91bD5cbiAgICA8L0ZyYWdtZW50PlxuICApO1xufVxuIl19
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/inline/index.js new
+57
@@ -0,0 +1,57 @@
1 +"use strict";
2 +
3 +Object.defineProperty(exports, "__esModule", {
4 + value: true
5 +});
6 +Object.defineProperty(exports, "ComponentWithCustomHook", {
7 + enumerable: true,
8 + get: function () {
9 + return _ComponentWithCustomHook.Component;
10 + }
11 +});
12 +Object.defineProperty(exports, "ComponentWithExternalCustomHooks", {
13 + enumerable: true,
14 + get: function () {
15 + return _ComponentWithExternalCustomHooks.Component;
16 + }
17 +});
18 +Object.defineProperty(exports, "Example", {
19 + enumerable: true,
20 + get: function () {
21 + return _Example.Component;
22 + }
23 +});
24 +Object.defineProperty(exports, "InlineRequire", {
25 + enumerable: true,
26 + get: function () {
27 + return _InlineRequire.Component;
28 + }
29 +});
30 +Object.defineProperty(exports, "useTheme", {
31 + enumerable: true,
32 + get: function () {
33 + return _useTheme.default;
34 + }
35 +});
36 +exports.ToDoList = void 0;
37 +
38 +var _ComponentWithCustomHook = require("./ComponentWithCustomHook");
39 +
40 +var _ComponentWithExternalCustomHooks = require("./ComponentWithExternalCustomHooks");
41 +
42 +var _Example = require("./Example");
43 +
44 +var _InlineRequire = require("./InlineRequire");
45 +
46 +var ToDoList = _interopRequireWildcard(require("./ToDoList"));
47 +
48 +exports.ToDoList = ToDoList;
49 +
50 +var _useTheme = _interopRequireDefault(require("./useTheme"));
51 +
52 +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
53 +
54 +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
55 +
56 +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; }
57 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFTQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7OztBQUVBIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIEZhY2Vib29rLCBJbmMuIGFuZCBpdHMgYWZmaWxpYXRlcy5cbiAqXG4gKiBUaGlzIHNvdXJjZSBjb2RlIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgbGljZW5zZSBmb3VuZCBpbiB0aGVcbiAqIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBkaXJlY3Rvcnkgb2YgdGhpcyBzb3VyY2UgdHJlZS5cbiAqXG4gKiBAZmxvd1xuICovXG5cbmV4cG9ydCB7Q29tcG9uZW50IGFzIENvbXBvbmVudFdpdGhDdXN0b21Ib29rfSBmcm9tICcuL0NvbXBvbmVudFdpdGhDdXN0b21Ib29rJztcbmV4cG9ydCB7Q29tcG9uZW50IGFzIENvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzfSBmcm9tICcuL0NvbXBvbmVudFdpdGhFeHRlcm5hbEN1c3RvbUhvb2tzJztcbmV4cG9ydCB7Q29tcG9uZW50IGFzIEV4YW1wbGV9IGZyb20gJy4vRXhhbXBsZSc7XG5leHBvcnQge0NvbXBvbmVudCBhcyBJbmxpbmVSZXF1aXJlfSBmcm9tICcuL0lubGluZVJlcXVpcmUnO1xuaW1wb3J0ICogYXMgVG9Eb0xpc3QgZnJvbSAnLi9Ub0RvTGlzdCc7XG5leHBvcnQge1RvRG9MaXN0fTtcbmV4cG9ydCB7ZGVmYXVsdCBhcyB1c2VUaGVtZX0gZnJvbSAnLi91c2VUaGVtZSc7XG4iXX0=
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/__compiled__/inline/useTheme.js
+1 -2
@@ -24,5 +24,4 @@ function useTheme() {
24 (0, _react.useDebugValue)(theme);
25 return theme;
26 }
27 -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInVzZVRoZW1lLmpzIl0sIm5hbWVzIjpbIlRoZW1lQ29udGV4dCIsInVzZVRoZW1lIiwidGhlbWUiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBU0E7O0FBVEE7Ozs7Ozs7O0FBV08sTUFBTUEsWUFBWSxnQkFBRywwQkFBYyxRQUFkLENBQXJCOzs7QUFFUSxTQUFTQyxRQUFULEdBQW9CO0FBQ2pDLFFBQU1DLEtBQUssR0FBRyx1QkFBV0YsWUFBWCxDQUFkO0FBQ0EsNEJBQWNFLEtBQWQ7QUFDQSxTQUFPQSxLQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAoYykgRmFjZWJvb2ssIEluYy4gYW5kIGl0cyBhZmZpbGlhdGVzLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLlxuICpcbiAqIEBmbG93XG4gKi9cblxuaW1wb3J0IHtjcmVhdGVDb250ZXh0LCB1c2VDb250ZXh0LCB1c2VEZWJ1Z1ZhbHVlfSBmcm9tICdyZWFjdCc7XG5cbmV4cG9ydCBjb25zdCBUaGVtZUNvbnRleHQgPSBjcmVhdGVDb250ZXh0KCdicmlnaHQnKTtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdXNlVGhlbWUoKSB7XG4gIGNvbnN0IHRoZW1lID0gdXNlQ29udGV4dChUaGVtZUNvbnRleHQpO1xuICB1c2VEZWJ1Z1ZhbHVlKHRoZW1lKTtcbiAgcmV0dXJuIHRoZW1lO1xufVxuIl19
28 -//# sourceURL=useTheme.js
\ No newline at end of file
27 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInVzZVRoZW1lLmpzIl0sIm5hbWVzIjpbIlRoZW1lQ29udGV4dCIsInVzZVRoZW1lIiwidGhlbWUiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBU0E7O0FBVEE7Ozs7Ozs7O0FBV08sTUFBTUEsWUFBWSxnQkFBRywwQkFBYyxRQUFkLENBQXJCOzs7QUFFUSxTQUFTQyxRQUFULEdBQW9CO0FBQ2pDLFFBQU1DLEtBQUssR0FBRyx1QkFBV0YsWUFBWCxDQUFkO0FBQ0EsNEJBQWNFLEtBQWQ7QUFDQSxTQUFPQSxLQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAoYykgRmFjZWJvb2ssIEluYy4gYW5kIGl0cyBhZmZpbGlhdGVzLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogTElDRU5TRSBmaWxlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGlzIHNvdXJjZSB0cmVlLlxuICpcbiAqIEBmbG93XG4gKi9cblxuaW1wb3J0IHtjcmVhdGVDb250ZXh0LCB1c2VDb250ZXh0LCB1c2VEZWJ1Z1ZhbHVlfSBmcm9tICdyZWFjdCc7XG5cbmV4cG9ydCBjb25zdCBUaGVtZUNvbnRleHQgPSBjcmVhdGVDb250ZXh0KCdicmlnaHQnKTtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdXNlVGhlbWUoKSB7XG4gIGNvbnN0IHRoZW1lID0gdXNlQ29udGV4dChUaGVtZUNvbnRleHQpO1xuICB1c2VEZWJ1Z1ZhbHVlKHRoZW1lKTtcbiAgcmV0dXJuIHRoZW1lO1xufVxuIl19
\ No newline at end of file
packages/react-devtools-extensions/src/__tests__/__source__/index.js new
+16
@@ -0,0 +1,16 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export {Component as ComponentWithCustomHook} from './ComponentWithCustomHook';
11 +export {Component as ComponentWithExternalCustomHooks} from './ComponentWithExternalCustomHooks';
12 +export {Component as Example} from './Example';
13 +export {Component as InlineRequire} from './InlineRequire';
14 +import * as ToDoList from './ToDoList';
15 +export {ToDoList};
16 +export {default as useTheme} from './useTheme';
packages/react-devtools-extensions/src/__tests__/parseHookNames-test.js
+29 -28
@@ -33,25 +33,18 @@ describe('parseHookNames', () => {
33
34 // Jest (jest-runner?) configures Errors to automatically account for source maps.
35 // This changes behavior between our tests and the browser.
36 - // To "fix" this, clear the prepareStackTrace() method on the Error object.
37 - delete Error.prepareStackTrace;
36 + // Ideally we would clear the prepareStackTrace() method on the Error object,
37 + // but Node falls back to looking for it on the main context's Error constructor,
38 + // which may still be patched.
39 + // To ensure we get the default behavior, override prepareStackTrace ourselves.
40 + // NOTE: prepareStackTrace is called from the error.stack getter, but the getter
41 + // has a recursion breaker which falls back to the default behavior.
42 + Error.prepareStackTrace = (error, trace) => {
43 + return error.stack;
44 + };
45
46 fetchMock.mockIf(/.+$/, request => {
40 - const {resolve} = require('path');
41 - const url = request.url;
42 - if (url.endsWith('js.map')) {
43 - // Source maps are relative URLs (e.g. "path/to/Exmaple.js" specifies "Exmaple.js.map").
44 - const sourceMapURL = resolve(
45 - __dirname,
46 - '__source__',
47 - '__compiled__',
48 - 'external',
49 - url,
50 - );
51 - return Promise.resolve(requireText(sourceMapURL, 'utf8'));
52 - } else {
53 - return Promise.resolve(requireText(url, 'utf8'));
54 - }
47 + return Promise.resolve(requireText(request.url, 'utf8'));
48 });
49
50 // Mock out portion of browser API used by parseHookNames to initialize "source-map".
@@ -185,10 +178,10 @@ describe('parseHookNames', () => {
178 ]);
179 });
180
188 - describe('inline and external source maps', () => {
181 + describe('inline, external and bundle source maps', () => {
182 it('should work for simple components', async () => {
190 - async function test(path) {
191 - const Component = require(path).Component;
183 + async function test(path, name = 'Component') {
184 + const Component = require(path)[name];
185 const hookNames = await getHookNamesForComponent(Component);
186 expectHookNamesToEqual(hookNames, [
187 'count', // useState
@@ -198,11 +191,12 @@ describe('parseHookNames', () => {
191 await test('./__source__/Example'); // original source (uncompiled)
192 await test('./__source__/__compiled__/inline/Example'); // inline source map
193 await test('./__source__/__compiled__/external/Example'); // external source map
194 + await test('./__source__/__compiled__/bundle/index', 'Example'); // bundle source map
195 });
196
197 it('should work with more complex files and components', async () => {
204 - async function test(path) {
205 - const components = require(path);
198 + async function test(path, name = undefined) {
199 + const components = name != null ? require(path)[name] : require(path);
200
201 let hookNames = await getHookNamesForComponent(components.List);
202 expectHookNamesToEqual(hookNames, [
@@ -228,11 +222,12 @@ describe('parseHookNames', () => {
222 await test('./__source__/ToDoList'); // original source (uncompiled)
223 await test('./__source__/__compiled__/inline/ToDoList'); // inline source map
224 await test('./__source__/__compiled__/external/ToDoList'); // external source map
225 + await test('./__source__/__compiled__/bundle', 'ToDoList'); // bundle source map
226 });
227
228 it('should work for custom hook', async () => {
234 - async function test(path) {
235 - const Component = require(path).Component;
229 + async function test(path, name = 'Component') {
230 + const Component = require(path)[name];
231 const hookNames = await getHookNamesForComponent(Component);
232 expectHookNamesToEqual(hookNames, [
233 'count', // useState()
@@ -244,11 +239,12 @@ describe('parseHookNames', () => {
239 await test('./__source__/ComponentWithCustomHook'); // original source (uncompiled)
240 await test('./__source__/__compiled__/inline/ComponentWithCustomHook'); // inline source map
241 await test('./__source__/__compiled__/external/ComponentWithCustomHook'); // external source map
242 + await test('./__source__/__compiled__/bundle', 'ComponentWithCustomHook'); // bundle source map
243 });
244
245 it('should work for external hooks', async () => {
250 - async function test(path) {
251 - const Component = require(path).Component;
246 + async function test(path, name = 'Component') {
247 + const Component = require(path)[name];
248 const hookNames = await getHookNamesForComponent(Component);
249 expectHookNamesToEqual(hookNames, [
250 'theme', // useTheme()
@@ -265,14 +261,18 @@ describe('parseHookNames', () => {
261 await test(
262 './__source__/__compiled__/external/ComponentWithExternalCustomHooks',
263 ); // external source map
264 + await test(
265 + './__source__/__compiled__/bundle',
266 + 'ComponentWithExternalCustomHooks',
267 + ); // bundle source map
268 });
269
270 // TODO Inline require (e.g. require("react").useState()) isn't supported yet.
271 // Maybe this isn't an important use case to support,
272 // since inline requires are most likely to exist in compiled source (if at all).
273 xit('should work for inline requires', async () => {
274 - async function test(path) {
275 - const Component = require(path).Component;
274 + async function test(path, name = 'Component') {
275 + const Component = require(path)[name];
276 const hookNames = await getHookNamesForComponent(Component);
277 expectHookNamesToEqual(hookNames, [
278 'count', // useState()
@@ -282,6 +282,7 @@ describe('parseHookNames', () => {
282 await test('./__source__/InlineRequire'); // original source (uncompiled)
283 await test('./__source__/__compiled__/inline/InlineRequire'); // inline source map
284 await test('./__source__/__compiled__/external/InlineRequire'); // external source map
285 + await test('./__source__/__compiled__/bundle', 'InlineRequire'); // bundle source map
286 });
287 });
288 });
packages/react-devtools-extensions/src/__tests__/updateMockSourceMaps.js
+35 -5
@@ -9,16 +9,23 @@ const {
9 } = require('fs');
10 const {emptyDirSync} = require('fs-extra');
11 const {resolve} = require('path');
12 +const rollup = require('rollup');
13 +const babel = require('rollup-plugin-babel');
14 +const commonjs = require('rollup-plugin-commonjs');
15 +const jsx = require('acorn-jsx');
16 +const rollupResolve = require('rollup-plugin-node-resolve');
17
18 const sourceDir = resolve(__dirname, '__source__');
19 const buildRoot = resolve(sourceDir, '__compiled__');
20 const externalDir = resolve(buildRoot, 'external');
21 const inlineDir = resolve(buildRoot, 'inline');
22 +const bundleDir = resolve(buildRoot, 'bundle');
23
24 // Remove previous builds
25 emptyDirSync(buildRoot);
26 mkdirSync(externalDir);
27 mkdirSync(inlineDir);
28 +mkdirSync(bundleDir);
29
30 function compile(fileName) {
31 const code = readFileSync(resolve(sourceDir, fileName), 'utf8');
@@ -44,9 +51,7 @@ function compile(fileName) {
51 // Generate compiled output with external source maps
52 writeFileSync(
53 resolve(externalDir, fileName),
47 - transformed.code +
48 - `\n//# sourceMappingURL=${fileName}.map` +
49 - `\n//# sourceURL=${fileName}`,
54 + transformed.code + `\n//# sourceMappingURL=${fileName}.map`,
55 'utf8',
56 );
57 writeFileSync(
@@ -60,12 +65,32 @@ function compile(fileName) {
65 resolve(inlineDir, fileName),
66 transformed.code +
67 '\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
63 - btoa(JSON.stringify(sourceMap)) +
64 - `\n//# sourceURL=${fileName}`,
68 + btoa(JSON.stringify(sourceMap)),
69 'utf8',
70 );
71 }
72
73 +async function bundle() {
74 + const entryFileName = resolve(sourceDir, 'index.js');
75 +
76 + // Bundle all modules with rollup
77 + const result = await rollup.rollup({
78 + input: entryFileName,
79 + acornInjectPlugins: [jsx()],
80 + plugins: [
81 + rollupResolve(),
82 + commonjs(),
83 + babel({presets: ['@babel/preset-react'], sourceMap: true}),
84 + ],
85 + external: ['react'],
86 + });
87 + await result.write({
88 + file: resolve(bundleDir, 'index.js'),
89 + format: 'cjs',
90 + sourcemap: true,
91 + });
92 +}
93 +
94 // Compile all files in the current directory
95 const entries = readdirSync(sourceDir);
96 entries.forEach(entry => {
@@ -74,3 +99,8 @@ entries.forEach(entry => {
99 compile(entry);
100 }
101 });
102 +
103 +bundle().catch(e => {
104 + console.error(e);
105 + process.exit(1);
106 +});
packages/react-devtools-extensions/src/astUtils.js
-8
@@ -20,14 +20,6 @@ export type SourceFileASTWithHookDetails = {
20 source: string,
21 };
22
23 -export type SourceMap = {|
24 - mappings: string,
25 - names: Array<string>,
26 - sources: Array<string>,
27 - sourcesContent: Array<string>,
28 - version: number,
29 -|};
30 -
23 const AST_NODE_TYPES = Object.freeze({
24 CALL_EXPRESSION: 'CallExpression',
25 MEMBER_EXPRESSION: 'MemberExpression',
packages/react-devtools-extensions/src/parseHookNames.js
+266 -153
@@ -24,7 +24,7 @@ import type {
24 } from 'react-debug-tools/src/ReactDebugHooks';
25 import type {HookNames, LRUCache} from 'react-devtools-shared/src/types';
26 import type {Thenable} from 'shared/ReactTypes';
27 -import type {SourceConsumer, SourceMap} from './astUtils';
27 +import type {SourceConsumer} from './astUtils';
28
29 const SOURCE_MAP_REGEX = / ?sourceMappingURL=([^\s'"]+)/gm;
30 const ABSOLUTE_URL_REGEX = /^https?:\/\//i;
@@ -43,36 +43,36 @@ type HookSourceData = {|
43 // If no source map has been provided, this code will be the same as runtimeSourceCode.
44 originalSourceCode: string | null,
45
46 + // Original source URL if there is a source map, or the same as runtimeSourceURL.
47 + originalSourceURL: string | null,
48 +
49 // Compiled code (React components or custom hooks) containing primitive hook calls.
50 runtimeSourceCode: string | null,
51
52 + // Same as hookSource.fileName but guaranteed to be non-null.
53 + runtimeSourceURL: string,
54 +
55 // APIs from source-map for parsing source maps (if detected).
56 sourceConsumer: SourceConsumer | null,
57
58 // External URL of source map.
59 // Sources without source maps (or with inline source maps) won't have this.
60 sourceMapURL: string | null,
55 -
56 - // Parsed source map object.
57 - sourceMapContents: SourceMap | null,
61 |};
62
60 -type CachedMetadata = {|
61 - originalSourceAST: AST,
62 - originalSourceCode: string,
63 +type CachedRuntimeCodeMetadata = {|
64 sourceConsumer: SourceConsumer | null,
65 |};
66
66 -// On large trees, encoding takes significant time.
67 -// Try to reuse the already encoded strings.
68 -const fileNameToMetadataCache: LRUCache<string, CachedMetadata> = new LRU({
67 +const runtimeURLToMetadataCache: LRUCache<
68 + string,
69 + CachedRuntimeCodeMetadata,
70 +> = new LRU({
71 max: 50,
70 - dispose: (fileName: string, metadata: CachedMetadata) => {
72 + dispose: (runtimeSourceURL: string, metadata: CachedRuntimeCodeMetadata) => {
73 if (__DEBUG__) {
74 console.log(
73 - 'fileNameToHookSourceData.dispose() Evicting cached metadata for "' +
74 - fileName +
75 - '"',
75 + `runtimeURLToMetadataCache.dispose() Evicting cached metadata for "${runtimeSourceURL}"`,
76 );
77 }
78
@@ -83,6 +83,36 @@ const fileNameToMetadataCache: LRUCache<string, CachedMetadata> = new LRU({
83 },
84 });
85
86 +type CachedSourceCodeMetadata = {|
87 + originalSourceAST: AST,
88 + originalSourceCode: string,
89 +|};
90 +
91 +const originalURLToMetadataCache: LRUCache<
92 + string,
93 + CachedSourceCodeMetadata,
94 +> = new LRU({
95 + max: 50,
96 + dispose: (originalSourceURL: string, metadata: CachedSourceCodeMetadata) => {
97 + if (__DEBUG__) {
98 + console.log(
99 + `originalURLToMetadataCache.dispose() Evicting cached metadata for "${originalSourceURL}"`,
100 + );
101 + }
102 + },
103 +});
104 +
105 +function getLocationKey({
106 + fileName,
107 + lineNumber,
108 + columnNumber,
109 +}: HookSource): string {
110 + if (fileName == null || lineNumber == null || columnNumber == null) {
111 + throw Error('Hook source code location not found.');
112 + }
113 + return `${fileName}:${lineNumber}:${columnNumber}`;
114 +}
115 +
116 export default async function parseHookNames(
117 hooksTree: HooksTree,
118 ): Thenable<HookNames | null> {
@@ -97,8 +127,8 @@ export default async function parseHookNames(
127 console.log('parseHookNames() hooksList:', hooksList);
128 }
129
100 - // Gather the unique set of source files to load for the built-in hooks.
101 - const fileNameToHookSourceData: Map<string, HookSourceData> = new Map();
130 + // Create map of unique source locations (file names plus line and column numbers) to metadata about hooks.
131 + const locationKeyToHookSourceData: Map<string, HookSourceData> = new Map();
132 for (let i = 0; i < hooksList.length; i++) {
133 const hook = hooksList[i];
134
@@ -109,50 +139,47 @@ export default async function parseHookNames(
139 throw Error('Hook source code location not found.');
140 }
141
112 - const fileName = hookSource.fileName;
113 - if (fileName == null) {
114 - throw Error('Hook source code location not found.');
115 - } else {
116 - if (!fileNameToHookSourceData.has(fileName)) {
117 - const hookSourceData: HookSourceData = {
118 - hookSource,
119 - originalSourceAST: null,
120 - originalSourceCode: null,
121 - runtimeSourceCode: null,
122 - sourceConsumer: null,
123 - sourceMapURL: null,
124 - sourceMapContents: null,
125 - };
126 -
127 - // If we've already loaded source/source map info for this file,
128 - // we can skip reloading it (and more importantly, re-parsing it).
129 - const metadata = fileNameToMetadataCache.get(fileName);
130 - if (metadata != null) {
131 - if (__DEBUG__) {
132 - console.groupCollapsed(
133 - 'parseHookNames() Found cached metadata for file "' +
134 - fileName +
135 - '"',
136 - );
137 - console.log(metadata);
138 - console.groupEnd();
139 - }
140 -
141 - hookSourceData.originalSourceAST = metadata.originalSourceAST;
142 - hookSourceData.originalSourceCode = metadata.originalSourceCode;
143 - hookSourceData.sourceConsumer = metadata.sourceConsumer;
142 + const locationKey = getLocationKey(hookSource);
143 + if (!locationKeyToHookSourceData.has(locationKey)) {
144 + // Can't be null because getLocationKey() would have thrown
145 + const runtimeSourceURL = ((hookSource.fileName: any): string);
146 +
147 + const hookSourceData: HookSourceData = {
148 + hookSource,
149 + originalSourceAST: null,
150 + originalSourceCode: null,
151 + originalSourceURL: null,
152 + runtimeSourceCode: null,
153 + runtimeSourceURL,
154 + sourceConsumer: null,
155 + sourceMapURL: null,
156 + };
157 +
158 + // If we've already loaded the source map info for this file,
159 + // we can skip reloading it (and more importantly, re-parsing it).
160 + const runtimeMetadata = runtimeURLToMetadataCache.get(
161 + hookSourceData.runtimeSourceURL,
162 + );
163 + if (runtimeMetadata != null) {
164 + if (__DEBUG__) {
165 + console.groupCollapsed(
166 + `parseHookNames() Found cached runtime metadata for file "${hookSourceData.runtimeSourceURL}"`,
167 + );
168 + console.log(runtimeMetadata);
169 + console.groupEnd();
170 }
145 -
146 - fileNameToHookSourceData.set(fileName, hookSourceData);
171 + hookSourceData.sourceConsumer = runtimeMetadata.sourceConsumer;
172 }
173 +
174 + locationKeyToHookSourceData.set(locationKey, hookSourceData);
175 }
176 }
177
151 - return loadSourceFiles(fileNameToHookSourceData)
152 - .then(() => extractAndLoadSourceMaps(fileNameToHookSourceData))
153 - .then(() => parseSourceAST(fileNameToHookSourceData))
154 - .then(() => updateLruCache(fileNameToHookSourceData))
155 - .then(() => findHookNames(hooksList, fileNameToHookSourceData));
178 + return loadSourceFiles(locationKeyToHookSourceData)
179 + .then(() => extractAndLoadSourceMaps(locationKeyToHookSourceData))
180 + .then(() => parseSourceAST(locationKeyToHookSourceData))
181 + .then(() => updateLruCache(locationKeyToHookSourceData))
182 + .then(() => findHookNames(hooksList, locationKeyToHookSourceData));
183 }
184
185 function decodeBase64String(encoded: string): Object {
@@ -170,12 +197,31 @@ function decodeBase64String(encoded: string): Object {
197 }
198
199 function extractAndLoadSourceMaps(
173 - fileNameToHookSourceData: Map<string, HookSourceData>,
200 + locationKeyToHookSourceData: Map<string, HookSourceData>,
201 ): Promise<*> {
175 - const promises = [];
176 - fileNameToHookSourceData.forEach(hookSourceData => {
177 - if (hookSourceData.originalSourceAST !== null) {
178 - // Use cached metadata.
202 + // SourceMapConsumer.initialize() does nothing when running in Node (aka Jest)
203 + // because the wasm file is automatically read from the file system
204 + // so we can avoid triggering a warning message about this.
205 + if (!__TEST__) {
206 + if (__DEBUG__) {
207 + console.log(
208 + 'extractAndLoadSourceMaps() Initializing source-map library ...',
209 + );
210 + }
211 +
212 + // $FlowFixMe
213 + const wasmMappingsURL = chrome.extension.getURL('mappings.wasm');
214 +
215 + SourceMapConsumer.initialize({'lib/mappings.wasm': wasmMappingsURL});
216 + }
217 +
218 + // Deduplicate fetches, since there can be multiple location keys per source map.
219 + const fetchPromises = new Map();
220 +
221 + const setPromises = [];
222 + locationKeyToHookSourceData.forEach(hookSourceData => {
223 + if (hookSourceData.sourceConsumer != null) {
224 + // Use cached source map consumer.
225 return;
226 }
227
@@ -189,9 +235,13 @@ function extractAndLoadSourceMaps(
235 }
236 } else {
237 for (let i = 0; i < sourceMappingURLs.length; i++) {
238 + const {runtimeSourceURL} = hookSourceData;
239 const sourceMappingURL = sourceMappingURLs[i];
240 const index = sourceMappingURL.indexOf('base64,');
241 if (index >= 0) {
242 + // TODO (named hooks) deduplicate parsing in this branch (similar to fetching in the other branch)
243 + // since there can be multiple location keys per source map.
244 +
245 // Web apps like Code Sandbox embed multiple inline source maps.
246 // In this case, we need to loop through and find the right one.
247 // We may also need to trim any part of this string that isn't based64 encoded data.
@@ -211,13 +261,17 @@ function extractAndLoadSourceMaps(
261
262 // Hook source might be a URL like "https://4syus.csb.app/src/App.js"
263 // Parsed source map might be a partial path like "src/App.js"
214 - const fileName = ((hookSourceData.hookSource.fileName: any): string);
264 const match = parsed.sources.find(
265 source =>
217 - source === 'Inline Babel script' || fileName.includes(source),
266 + source === 'Inline Babel script' ||
267 + runtimeSourceURL.endsWith(source),
268 );
269 if (match) {
220 - hookSourceData.sourceMapContents = parsed;
270 + setPromises.push(
271 + new SourceMapConsumer(parsed).then(sourceConsumer => {
272 + hookSourceData.sourceConsumer = sourceConsumer;
273 + }),
274 + );
275 break;
276 }
277 } else {
@@ -227,7 +281,7 @@ function extractAndLoadSourceMaps(
281 );
282 }
283
230 - let url = sourceMappingURLs[0].split('=')[1];
284 + let url = sourceMappingURLs[i].split('=')[1];
285 if (ABSOLUTE_URL_REGEX.test(url)) {
286 const baseURL = url.slice(0, url.lastIndexOf('/'));
287 url = `${baseURL}/${url}`;
@@ -235,19 +289,38 @@ function extractAndLoadSourceMaps(
289 if (!isValidUrl(url)) {
290 throw new Error(`Invalid source map URL "${url}"`);
291 }
292 + } else if (!url.startsWith('/')) {
293 + // Resolve paths relative to the location of the file name
294 + const lastSlashIdx = runtimeSourceURL.lastIndexOf('/');
295 + if (lastSlashIdx !== -1) {
296 + const baseURL = runtimeSourceURL.slice(
297 + 0,
298 + runtimeSourceURL.lastIndexOf('/'),
299 + );
300 + url = `${baseURL}/${url}`;
301 + }
302 }
303
304 hookSourceData.sourceMapURL = url;
305
242 - if (__DEBUG__) {
243 - console.log(
244 - 'extractAndLoadSourceMaps() External source map "' + url + '"',
306 + const fetchPromise =
307 + fetchPromises.get(url) ||
308 + fetchFile(url).then(
309 + sourceMapContents =>
310 + new SourceMapConsumer(JSON.parse(sourceMapContents)),
311 );
312 + if (__DEBUG__) {
313 + if (!fetchPromises.has(url)) {
314 + console.log(
315 + `extractAndLoadSourceMaps() External source map "${url}"`,
316 + );
317 + }
318 }
319
248 - promises.push(
249 - fetchFile(url).then(sourceMapContents => {
250 - hookSourceData.sourceMapContents = JSON.parse(sourceMapContents);
320 + fetchPromises.set(url, fetchPromise);
321 + setPromises.push(
322 + fetchPromise.then(sourceConsumer => {
323 + hookSourceData.sourceConsumer = sourceConsumer;
324 }),
325 );
326 break;
@@ -255,7 +328,7 @@ function extractAndLoadSourceMaps(
328 }
329 }
330 });
258 - return Promise.all(promises);
331 + return Promise.all(setPromises);
332 }
333
334 function fetchFile(url: string): Promise<string> {
@@ -268,9 +341,15 @@ function fetchFile(url: string): Promise<string> {
341 resolve(text);
342 })
343 .catch(error => {
344 + if (__DEBUG__) {
345 + console.log(`fetchFile() Could not read text for url "${url}"`);
346 + }
347 reject(null);
348 });
349 } else {
350 + if (__DEBUG__) {
351 + console.log(`fetchFile() Got bad response for url "${url}"`);
352 + }
353 reject(null);
354 }
355 });
@@ -279,11 +358,13 @@ function fetchFile(url: string): Promise<string> {
358
359 function findHookNames(
360 hooksList: Array<HooksNode>,
282 - fileNameToHookSourceData: Map<string, HookSourceData>,
361 + locationKeyToHookSourceData: Map<string, HookSourceData>,
362 ): HookNames {
363 const map: HookNames = new Map();
364
365 hooksList.map(hook => {
366 + // TODO (named hooks) We should probably filter before this point,
367 + // otherwise we are loading and parsing source maps and ASTs for nothing.
368 if (isNonDeclarativePrimitiveHook(hook)) {
369 if (__DEBUG__) {
370 console.log('findHookNames() Non declarative primitive hook');
@@ -300,7 +381,8 @@ function findHookNames(
381 return null; // Should not be reachable.
382 }
383
303 - const hookSourceData = fileNameToHookSourceData.get(fileName);
384 + const locationKey = getLocationKey(hookSource);
385 + const hookSourceData = locationKeyToHookSourceData.get(locationKey);
386 if (!hookSourceData) {
387 return null; // Should not be reachable.
388 }
@@ -321,7 +403,10 @@ function findHookNames(
403 } else {
404 originalSourceLineNumber = sourceConsumer.originalPositionFor({
405 line: lineNumber,
324 - column: columnNumber,
406 +
407 + // Column numbers are representated differently between tools/engines.
408 + // For more info see https://github.com/facebook/react/issues/21792#issuecomment-873171991
409 + column: columnNumber - 1,
410 }).line;
411 }
412
@@ -346,7 +431,7 @@ function findHookNames(
431 );
432
433 if (__DEBUG__) {
349 - console.log('findHookNames() Found name "' + (name || '-') + '"');
434 + console.log(`findHookNames() Found name "${name || '-'}"`);
435 }
436
437 map.set(hook, name);
@@ -366,103 +451,133 @@ function isValidUrl(possibleURL: string): boolean {
451 }
452
453 function loadSourceFiles(
369 - fileNameToHookSourceData: Map<string, HookSourceData>,
454 + locationKeyToHookSourceData: Map<string, HookSourceData>,
455 ): Promise<*> {
371 - const promises = [];
372 - fileNameToHookSourceData.forEach((hookSourceData, fileName) => {
373 - promises.push(
374 - fetchFile(fileName).then(runtimeSourceCode => {
456 + // Deduplicate fetches, since there can be multiple location keys per file.
457 + const fetchPromises = new Map();
458 +
459 + const setPromises = [];
460 + locationKeyToHookSourceData.forEach(hookSourceData => {
461 + const {runtimeSourceURL} = hookSourceData;
462 + const fetchPromise =
463 + fetchPromises.get(runtimeSourceURL) ||
464 + fetchFile(runtimeSourceURL).then(runtimeSourceCode => {
465 if (runtimeSourceCode.length > MAX_SOURCE_LENGTH) {
466 throw Error('Source code too large to parse');
467 }
378 -
468 if (__DEBUG__) {
469 console.groupCollapsed(
381 - 'loadSourceFiles() fileName "' + fileName + '"',
470 + `loadSourceFiles() runtimeSourceURL "${runtimeSourceURL}"`,
471 );
472 console.log(runtimeSourceCode);
473 console.groupEnd();
474 }
386 -
475 + return runtimeSourceCode;
476 + });
477 + fetchPromises.set(runtimeSourceURL, fetchPromise);
478 + setPromises.push(
479 + fetchPromise.then(runtimeSourceCode => {
480 hookSourceData.runtimeSourceCode = runtimeSourceCode;
481 }),
482 );
483 });
391 - return Promise.all(promises);
484 + return Promise.all(setPromises);
485 }
486
487 async function parseSourceAST(
395 - fileNameToHookSourceData: Map<string, HookSourceData>,
488 + locationKeyToHookSourceData: Map<string, HookSourceData>,
489 ): Promise<*> {
397 - // SourceMapConsumer.initialize() does nothing when running in Node (aka Jest)
398 - // because the wasm file is automatically read from the file system
399 - // so we can avoid triggering a warning message about this.
400 - if (!__TEST__) {
401 - if (__DEBUG__) {
402 - console.log('parseSourceAST() Initializing source-map library ...');
403 - }
404 -
405 - // $FlowFixMe
406 - const wasmMappingsURL = chrome.extension.getURL('mappings.wasm');
407 -
408 - SourceMapConsumer.initialize({'lib/mappings.wasm': wasmMappingsURL});
409 - }
410 -
411 - const promises = [];
412 - fileNameToHookSourceData.forEach(hookSourceData => {
490 + locationKeyToHookSourceData.forEach(hookSourceData => {
491 if (hookSourceData.originalSourceAST !== null) {
492 // Use cached metadata.
493 return;
494 }
495
418 - const {runtimeSourceCode, sourceMapContents} = hookSourceData;
419 - if (sourceMapContents !== null) {
496 + const {sourceConsumer} = hookSourceData;
497 + const runtimeSourceCode = ((hookSourceData.runtimeSourceCode: any): string);
498 + let originalSourceURL, originalSourceCode;
499 + if (sourceConsumer !== null) {
500 // Parse and extract the AST from the source map.
421 - promises.push(
422 - SourceMapConsumer.with(
423 - sourceMapContents,
424 - null,
425 - (sourceConsumer: SourceConsumer) => {
426 - hookSourceData.sourceConsumer = sourceConsumer;
427 -
428 - // Now that the source map has been loaded,
429 - // extract the original source for later.
430 - const source = sourceMapContents.sources[0];
431 - const originalSourceCode = sourceConsumer.sourceContentFor(
432 - source,
433 - true,
434 - );
501 + const {lineNumber, columnNumber} = hookSourceData.hookSource;
502 + if (lineNumber == null || columnNumber == null) {
503 + throw Error('Hook source code location not found.');
504 + }
505 + // Now that the source map has been loaded,
506 + // extract the original source for later.
507 + const {source} = sourceConsumer.originalPositionFor({
508 + line: lineNumber,
509
436 - if (__DEBUG__) {
437 - console.groupCollapsed(
438 - 'parseSourceAST() Extracted source code from source map',
439 - );
440 - console.log(originalSourceCode);
441 - console.groupEnd();
442 - }
510 + // Column numbers are representated differently between tools/engines.
511 + // For more info see https://github.com/facebook/react/issues/21792#issuecomment-873171991
512 + column: columnNumber - 1,
513 + });
514 +
515 + if (source == null) {
516 + // TODO (named hooks) maybe fall back to the runtime source instead of throwing?
517 + throw new Error(
518 + 'Could not map hook runtime location to original source location',
519 + );
520 + }
521
444 - hookSourceData.originalSourceCode = originalSourceCode;
522 + // TODO (named hooks) maybe canonicalize this URL somehow?
523 + // It can be relative if the source map specifies it that way,
524 + // but we use it as a cache key across different source maps and there can be collisions.
525 + originalSourceURL = (source: string);
526 + originalSourceCode = (sourceConsumer.sourceContentFor(
527 + source,
528 + true,
529 + ): string);
530
446 - // TODO Parsing should ideally be done off of the main thread.
447 - hookSourceData.originalSourceAST = parse(originalSourceCode, {
448 - sourceType: 'unambiguous',
449 - plugins: ['jsx', 'typescript'],
450 - });
451 - },
452 - ),
453 - );
531 + if (__DEBUG__) {
532 + console.groupCollapsed(
533 + 'parseSourceAST() Extracted source code from source map',
534 + );
535 + console.log(originalSourceCode);
536 + console.groupEnd();
537 + }
538 } else {
539 // There's no source map to parse here so we can just parse the original source itself.
456 - hookSourceData.originalSourceCode = runtimeSourceCode;
540 + originalSourceCode = runtimeSourceCode;
541 + // TODO (named hooks) This mixes runtimeSourceURLs with source mapped URLs in the same cache key space.
542 + // Namespace them?
543 + originalSourceURL = hookSourceData.runtimeSourceURL;
544 + }
545 +
546 + hookSourceData.originalSourceCode = originalSourceCode;
547 + hookSourceData.originalSourceURL = originalSourceURL;
548
458 - // TODO Parsing should ideally be done off of the main thread.
459 - hookSourceData.originalSourceAST = parse(runtimeSourceCode, {
549 + // The cache also serves to deduplicate parsing by URL in our loop over
550 + // location keys. This may need to change if we switch to async parsing.
551 + const sourceMetadata = originalURLToMetadataCache.get(originalSourceURL);
552 + if (sourceMetadata != null) {
553 + if (__DEBUG__) {
554 + console.groupCollapsed(
555 + `parseSourceAST() Found cached source metadata for "${originalSourceURL}"`,
556 + );
557 + console.log(sourceMetadata);
558 + console.groupEnd();
559 + }
560 + hookSourceData.originalSourceAST = sourceMetadata.originalSourceAST;
561 + hookSourceData.originalSourceCode = sourceMetadata.originalSourceCode;
562 + } else {
563 + // TODO (named hooks) Parsing should ideally be done off of the main thread.
564 + const originalSourceAST = parse(originalSourceCode, {
565 sourceType: 'unambiguous',
566 plugins: ['jsx', 'typescript'],
567 });
568 + hookSourceData.originalSourceAST = originalSourceAST;
569 + if (__DEBUG__) {
570 + console.log(
571 + `parseSourceAST() Caching source metadata for "${originalSourceURL}"`,
572 + );
573 + }
574 + originalURLToMetadataCache.set(originalSourceURL, {
575 + originalSourceAST,
576 + originalSourceCode,
577 + });
578 }
579 });
465 - return Promise.all(promises);
580 + return Promise.resolve();
581 }
582
583 function flattenHooksList(
@@ -479,23 +594,21 @@ function flattenHooksList(
594 }
595
596 function updateLruCache(
482 - fileNameToHookSourceData: Map<string, HookSourceData>,
597 + locationKeyToHookSourceData: Map<string, HookSourceData>,
598 ): Promise<*> {
484 - fileNameToHookSourceData.forEach(
485 - ({originalSourceAST, originalSourceCode, sourceConsumer}, fileName) => {
486 - // Only set once to avoid triggering eviction/cleanup code.
487 - if (!fileNameToMetadataCache.has(fileName)) {
488 - if (__DEBUG__) {
489 - console.log('updateLruCache() Caching metada for "' + fileName + '"');
490 - }
491 -
492 - fileNameToMetadataCache.set(fileName, {
493 - originalSourceAST,
494 - originalSourceCode: ((originalSourceCode: any): string),
495 - sourceConsumer,
496 - });
599 + locationKeyToHookSourceData.forEach(({sourceConsumer, runtimeSourceURL}) => {
600 + // Only set once to avoid triggering eviction/cleanup code.
601 + if (!runtimeURLToMetadataCache.has(runtimeSourceURL)) {
602 + if (__DEBUG__) {
603 + console.log(
604 + `updateLruCache() Caching runtime metadata for "${runtimeSourceURL}"`,
605 + );
606 }
498 - },
499 - );
607 +
608 + runtimeURLToMetadataCache.set(runtimeSourceURL, {
609 + sourceConsumer,
610 + });
611 + }
612 + });
613 return Promise.resolve();
614 }
packages/react-devtools-shared/src/hookNamesCache.js
+14 -1
@@ -9,6 +9,7 @@
9
10 import {unstable_getCacheForType as getCacheForType} from 'react';
11 import {enableHookNameParsing} from 'react-devtools-feature-flags';
12 +import {__DEBUG__} from 'react-devtools-shared/src/constants';
13
14 import type {HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
15 import type {Thenable, Wakeable} from 'shared/ReactTypes';
@@ -108,6 +109,10 @@ export function loadHookNames(
109 return;
110 }
111
112 + if (__DEBUG__) {
113 + console.log('[hookNamesCache] onSuccess() hookNames:', hookNames);
114 + }
115 +
116 if (hookNames) {
117 const resolvedRecord = ((newRecord: any): ResolvedRecord<HookNames>);
118 resolvedRecord.status = Resolved;
@@ -121,6 +126,10 @@ export function loadHookNames(
126 wake();
127 },
128 function onError(error) {
129 + if (__DEBUG__) {
130 + console.log('[hookNamesCache] onError() error:', error);
131 + }
132 +
133 if (didTimeout) {
134 return;
135 }
@@ -134,7 +143,11 @@ export function loadHookNames(
143 );
144
145 // Eventually timeout and stop trying to load names.
137 - let timeoutID = setTimeout(() => {
146 + let timeoutID = setTimeout(function onTimeout() {
147 + if (__DEBUG__) {
148 + console.log('[hookNamesCache] onTimeout()');
149 + }
150 +
151 timeoutID = null;
152
153 didTimeout = true;