24 Commits

Author SHA1 Message Date
Ruslan Lesiutin 6de7733e73 feat[devtools]: ship source maps for content scripts and ignore list installHook script (#28730)
## Summary

1. RDT browser extension's content scripts will now ship source maps
(without source in prod, to save some bundle size).
2. `installHook` content script will be ignore listed via `ignoreList`
field in the corresponding source map.
3. Previously, source map for backend file used `x_google_ignoreList`
naming, now `ignoreList`.

## How did you test this change?

1. `ignoreList-test.js`
2. Tested manually that I don't see `installHook` in stack traces when
`console.error` is called.
2024-04-08 18:10:09 +01:00
Ruslan Lesiutin 8ec962d825 fix[devtools/ci]: fixed incorrect condition calculation for @reactVersion annotation (#26997)
Suppose that you have this setup for devtools test:
```
// @reactVersion <= 18.1
// @reactVersion >= 17.1
```

With previous implementation, the accumulated condition will be `"<=
18.1" && ">= 17.1"`, which is just `">= 17.1"`, when evaluated. That's
why we executed some tests for old versions of react on main (and
failed).

With these changes the resulting condition will be `"<= 18.1 >= 17.1"`,
not using `&&`, because semver does not support this operator. All
currently failing tests will be skipped now as expected.

Also increased timeout value for shell server to start
2023-06-23 16:45:53 +01:00
Ruslan Lesiutin e3fb7c1de1 feat[devtools]: support x_google_ignoreList source maps extension (#26951)
## Summary
This was originally implemented by Mengdi @mondaychen in
https://github.com/facebook/react/pull/26506.

Because we patch console methods (to append components stack and some
other features), errors in console will include
`react_devtools_backend-....js` in its stack traces. Example:
<img width="763" alt="Screenshot 2023-06-15 at 13 31 49"
src="https://github.com/facebook/react/assets/28902667/fa9c3d26-b6c5-4965-af71-62d100cd806d">

Using https://github.com/mondaychen/devtools-ignore-webpack-plugin to
support [x_google_ignoreList source maps
extension](https://developer.chrome.com/blog/devtools-better-angular-debugging/#the-x_google_ignorelist-source-map-extension).

@mondaychen created a react app, which throws an error via
`console.error`, when user click on the button -
https://3owqsn.csb.app/.

Stack trace with these changes: 
<img width="759" alt="Screenshot 2023-06-14 at 14 26 38"
src="https://github.com/facebook/react/assets/28902667/b118b168-3200-4a47-9718-39fc455ea993">
2023-06-21 12:36:48 +01:00
Brian Vaughn e07039bb61 Moved named hooks code (and tests) from react-devtools-extensions to react-devtools-shared (#22260) 2021-09-07 11:44:49 -04:00
Brian Vaughn 9fc04eaf3f DevTools: Improve named hooks network caching (#22198)
While testing the recently-launched named hooks feature, I noticed that one of the two big performance bottlenecks is fetching the source file. This was unexpected since the source file has already been loaded by the page. (After all, DevTools is inspecting a component defined in that same file.)

To address this, I made the following changes:
- [x] Keep CPU bound work (parsing source map and AST) in a worker so it doesn't block the main thread but move I/O bound code (fetching files) to the main thread.
- [x] Inject a function into the page (as part of the content script) to fetch cached files for the extension. Communicate with this function using `eval()` (to send it messages) and `chrome.runtime.sendMessage()` to return its responses to the extension).

With the above changes in place, the extension gets cached responses from a lot of sites- but not Facebook. This seems to be due to the following:
* Facebook's response headers include [`vary: 'Origin'`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary).
* The `fetch` made from the content script does not include an `Origin` request header.

To reduce the impact of cases where we can't re-use the Network cache, this PR also makes additional changes:
- [x] Use `devtools.network.onRequestFinished` to (pre)cache resources as the page loads them. This allows us to avoid requesting a resource that's already been loaded in most cases.
- [x] In case DevTools was opened _after_ some requests were made, we also now pre-fetch (and cache in memory) source files when a component is selected (if it has hooks). If the component's hooks are later evaluated, the source map will be faster to access. (Note that in many cases, this prefetch is very fast since it is returned from the disk cache.)

With the above changes, we've reduced the time spent in `loadSourceFiles` to nearly nothing.
2021-09-01 14:10:07 -04:00
Juan 9e8fe11e11 [DevTools] Keep query params in extracted source map urls (#22148)
## Summary

Our current logic for extracting source map urls assumed that the url contained no query params (e.g. `?foo=bar`), and when extracting the url we would cut off the query params. I noticed this during internal testing, since removing the query params would cause loading source maps to fail.

This commit fixes that behavior by ensuring that our regex captures the full url, including query params.

## Test Plan

- yarn flow
- yarn test
- yarn test-build-devtools
- added new regression tests 
- named hooks still work on manual test of browser extension on a few different apps (code sandbox, create-react-app, internally).
2021-08-20 18:04:30 -04:00
Brian Vaughn 64f83a6fd2 Replace "source-map" library with "source-map-js" (#22126) 2021-08-19 11:25:38 -04:00
Juan 42ef45b129 [DevTools] Using array destructuring without assigning first variable does not error (#22129)
## Summary

Before this commit, if a hook returned an array the was destructured, but without assigning a variable to the first element in the array, this would produce an error. This was detected via internal testing.

This commit fixes that and adds regression tests.


## Test Plan

- yarn flow
- yarn test
- yarn test-build-devtools
- added new regression tests 
- named hooks still work on manual test of browser extension on a few different apps (code sandbox, create-react-app, internally).
2021-08-18 18:26:57 -04:00
Juan f1db9c30cc [DevTools] Support object destructuring pattern without runtime errors (#22128)
## Summary

Before this commit, if a hook returned an object and we declared a variable using object destructuring on the returned value, we would produce a runtime error. This was detected via internal testing.

This commit fixes that and adds regression tests.


## Test Plan

- yarn flow
- yarn test
- yarn test-build-devtools
- added new regression tests 
- named hooks still work on manual test of browser extension on a few different apps (code sandbox, create-react-app, internally).
2021-08-18 18:17:29 -04:00
Juan 6abda7f4f2 [DevTools] Access metadata in source maps correctly accounting for different formats (#22096)
## Summary

Follow up from https://github.com/facebook/react/pull/22010.

The initial implementation of named hooks and for looking up hook name metadata in an extended source map both assumed that the source maps would always have a `sources` field available, and didn't account for the source maps in the [Index Map](https://sourcemaps.info/spec.html#h.535es3xeprgt) format, which contain a list of `sections` and don't have the `source` field available directly. 

In order to properly access metadata in extended source maps, this commit:

-  Adds a new `SourceMapMetadataConsumer` api, which is a fork / very similar in structure to the corresponding [consumer in Metro](https://github.com/facebook/metro/blob/2b44ec39b4bca93e3e1cf1f268b4be66f894924a/packages/metro-symbolicate/src/SourceMetadataMapConsumer.js#L56) (as specified by @motiz88 in https://github.com/facebook/react/issues/21782.
- Updates `parseHookNames` to use this new api

## Test Plan

- yarn flow
- yarn test
- yarn test-build-devtools
- added new regression tests covering the index map format
- named hooks still work on manual test of browser extension on a few different apps (code sandbox, create-react-app, internally).
2021-08-18 14:30:31 -04:00
Luna Ruan f6ec4661db make parseHookNames a separate bundle (#22102) 2021-08-16 16:46:46 -04:00
Juan 531c97ef7a [DevTools] Updated source map extension format + more precise types (#22073)
## 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
2021-08-11 15:46:22 -04:00
Juan 88d121899a [DevTools] Support extended source maps with named hooks information (#22010)
## 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.
2021-08-11 10:46:19 -04:00
Juan b9934d6db5 [DevTools] Hook names are correctly extracted when parsing nested hook calls (#22037)
## 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). 
![image](https://user-images.githubusercontent.com/1271509/128409571-d62e0a74-6b7b-4c3f-ad86-6799ecd71962.png)

![image](https://user-images.githubusercontent.com/1271509/128409943-f898f27b-67ab-4260-a931-40d9c1942395.png)

![image](https://user-images.githubusercontent.com/1271509/128410326-79a0f822-55b1-4b90-a9b9-78f13fa0b5c5.png)
2021-08-05 16:36:59 -04:00
Juan b537247678 New devtools test for named hooks verifying case when hooks are used indirectly (#21996)
## 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
```
2021-07-30 17:10:46 -04:00
Lucas Correia 25f09e3e4e DevTools: Parse named source AST in a worker (#21902)
Resolves #21855

Ended up using workerize in order to setup the worker once it allows easy imports (for babel's parse function) and exports.
2021-07-21 12:16:08 -04:00
Brian Vaughn 6840c98c32 Remove named hooks feature flag (#21894) 2021-07-16 00:14:20 -04:00
Brian Vaughn e26cb8f86d Clear named hooks Suspense and AST cache after a Fast Refresh (#21891) 2021-07-15 23:39:30 -04:00
Brian Vaughn 87b3ada89d DevTools: Named hooks supports "cheap-module-source-map" (#21874)
"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.
2021-07-14 14:37:27 -04:00
Brian Vaughn 9fec3f2add DevTools: Ignore multiple sourceMappingUrls for external source maps (#21871)
Added an edge case regression test and bugfix.
2021-07-13 16:39:29 -04:00
Brian Vaughn 32d88d4332 DevTools: Don't load source files contaning only unnamed hooks (#21835)
This wastes CPU cycles.
2021-07-08 16:46:17 -04:00
Brian Vaughn f52b73f9d0 DevTools: Update named hooks match to use column number also (#21833)
This prevents edge cases where AST nodes are incorrectly matched.
2021-07-08 16:12:22 -04:00
Moti Zilberman 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>
2021-07-07 13:07:58 -04:00
Brian Vaughn c5cfa71948 DevTools: Show hook names based on variable usage (#21641)
Co-authored-by: Brian Vaughn <brian.david.vaughn@gmail.com>
Co-authored-by: Saphal Patro <saphal1998@gmail.com>
Co-authored-by: VibhorCodecianGupta <vibhordelgupta@gmail.com>
2021-07-01 14:39:18 -04:00