## Summary
Follow up from https://github.com/facebook/react/pull/22010.
As suggested by @motiz88, update the way the react sources metadata is stored within the fb sources metadata. Specifically, instead of `x_facebook_sources` directly containing a hook map in the second position of the metadata tuple for a given source, it contains the react sources metadata itself, which is also a tuple of react sources metadata for a given source, and which contains the hook map in the first position. This way the react sources metadata tuple can be extended to contain more react-specific metadata without taking up more positions in the top-level facebook sources metadata.
As part of this change:
- Adds more precise Flow types, mostly borrowed from Metro
- Fixes the facebook sources field name (we were using `x_fb_sources` but it should be `x_facebook_sources`
## Test Plan
- yarn flow
- yarn test
- yarn test-build-devtools
## Summary
Adds support for statically extracting names for hook calls from source code, and extending source maps with that information so that DevTools does not have to directly parse source code at runtime, which will speed up the Named Hooks feature and allow it to be enabled by default.
Specifically, this PR includes the following parts:
- [x] Adding logic to statically extract relevant hook names from the parsed source code (i.e. the babel ast). Note that this logic differs slightly from the existing logic in that the existing logic also uses runtime information from DevTools (such as whether given hooks are a custom hook) to extract names for hooks, whereas this code is meant to run entirely at build time, so it does not rely on that information.
- [x] Generating an encoded "hook map", which encodes the information about a hooks *original* source location, and it's corresponding name. This "hook map" will be used to generate extended source maps, included tentatively under an extra `x_react_hook_map` field. The map itself is formatted and encoded in a very similar way as how the `names` and `mappings` fields of a standard source map are encoded ( = Base64 VLQ delta coding representing offsets into a string array), and how the "function map" in Metro is encoded, as suggested in #21782. Note that this initial version uses a very basic format, and we are not implementing our own custom encoding, but reusing the `encode` function from `sourcemap-codec`.
- [x] Updating the logic in `parseHookNames` to check if the source maps have been extended with the hook map information, and if so use that information to extract the hook names without loading the original source code. In this PR we are manually generating extended source maps in our tests in order to test that this functionality works as expected, even though we are not actually generating the extended source maps in production.
The second stage of this work, which will likely need to occur outside this repo, is to update bundlers such as Metro to use these new primitives to actually generate source maps that DevTools can use.
### Follow-ups
- Enable named hooks by default when extended source maps are present
- Support looking up hook names when column numbers are not present in source map.
- Measure performance improvement of using extended source maps (manual testing suggests ~4 to 5x faster)
- Update relevant bundlers to generate extended source maps.
## Test Plan
- yarn flow
- Tests still pass
- yarn test
- yarn test-build-devtools
- Named hooks still work on manual test of browser extension on a few different apps (code sandbox, create-react-app, facebook).
- For new functionality:
- New tests for statically extracting hook names.
- New tests for using extended source maps to look up hook names at runtime.
## Summary
This commit fixes an issue where DevTools would currently not correctly extract the hook names for a hook call when the hook call was nested under *another* hook call, e.g.:
```javascript
function Component() {
const InnerComponent = useMemo(() => () => {
const [state, setState] = useState(0);
return state;
});
return null;
};
```
Although it seems pretty rare to encounter this case in actual product code, DevTools wasn't handling it correctly:
**Expected Names:**
- `InnerComponent` for the `useMemo()` call.
- `state` for the `useState()` call.
**Actual**
- `InnerComponent` for the `useMemo()` call.
- `InnerComponent` for the `useState()` call.
The reason that we were extracting the name for the nested hook call incorrectly is that the `checkNodeLocation` function (which attempts to check if the location of the hook matches the location in the original source code), was too "lenient" and would return a match even if the start lines of the locations didn't match.
Specifically, for our example, it would consider that the location of the outer hook call "matched" the location of the inner hook call (even though they were on different lines), and would then return the wrong hook name.
### Fix
The fix in this commit is to update the `checkNodeLocation` function to more strictly check for matching start lines. The assumption here is that if 2 locations are on different starting lines, they can't possibly correspond to the same hook call.
## Test Plan
- yarn flow
- Tests still pass
- yarn test
- yarn test-build-devtools
- new regression tests added
- named hooks still work on manual test of browser extension on a few different apps (code sandbox, create-react-app, internally).



## Summary
Adds a new unit test to `parseHookNames-test` which verifies that we correctly give names to hooks when they are used indirectly:
e.g.
```
const countState = useState(0);
const count = countState[0];
const setCount = countState[1];
```
Should produce `count` as the name.
## Test plan
```
yarn test
yarn test-build-devtools
yarn test-build-devtools parseHookNames
```
"cheap-module-source-map" is the default source-map generation mode used in created-react-dev mode because of speed. The major trade-off is that the source maps generated don't contain column numbers, so DevTools needs to be more lenient when matching AST nodes in this mode.
In this case, it can ignore column numbers and match nodes using line numbers only– so long as only a single node matches. If more than one match is found, treat it the same as if none were found, and fall back to no name.
Made several changes to the hooks name cache to avoid losing cached data between selected elements:
1. No longer use React-managed cache. This had the unfortunate side effect of the inspected element cache also clearing the hook names cache. For now, instead, a module-level WeakMap cache is used. This isn't great but we can revisit it later.
2. Hooks are no longer the cache keys (since hook objects get recreated between element inspections). Instead a hook key string made of fileName + line number + column number is used.
3. If hook names have already been loaded for a component, skip showing the load button and just show the hook names by default when selecting the component.
* 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>
The following APIs have been added to the `react` stable entry point:
* `SuspenseList`
* `startTransition`
* `unstable_createMutableSource`
* `unstable_useMutableSource`
* `useDeferredValue`
* `useTransition`
The following APIs have been added or removed from the `react-dom` stable entry point:
* `createRoot`
* `unstable_createPortal` (removed)
The following APIs have been added to the `react-is` stable entry point:
* `SuspenseList`
* `isSuspenseList`
The following feature flags have been changed from experimental to true:
* `enableLazyElements`
* `enableSelectiveHydration`
* `enableSuspenseServerRenderer`
We have been building DevTools to target Chrome 49 and Firefox 54. These are super old browser versions and they did not have full ES6 support, so the generated build is more bloated than it needs to be.
DevTools uses most modern language features. Off the top of my head, we it uses basically everything but async and generator functions.
Based on CanIUse charts– I believe that in order to avoid unnecessary polyfill/wrapper code being generated, we'd need to target Chrome 60+ (released 2017-07-25) and Firefox 55+ (released 2017-04-18). This seems like a reasonable set of browsers to target.
Note that we can't remove the IE 11 target from the react-devtools-core backend yet due to Hermes (React Native) ES6 support but that should be doable by the end of the year given current engineering targets. But we could update the frontend target, as well as the targets for the extensions and the react-devtools-inline package.
This commit increases the browser targets then for Chrome (from 49 to 60) and Firefox (from 54 to 55)