Commit Graph

14525 Commits

Author SHA1 Message Date
Sebastian Markbåge 2cc6d79c98
Rename onReadyToStream to onCompleteShell (#22443) 2021-09-27 12:38:22 -07:00
Brian Vaughn 2e41568313
DevTools encoding supports multibyte characters (e.g. "🟩") (#22424)
Changes our text encoding approach to properly support multibyte characters following this algorithm. Based on benchmarking, this new approach is roughly equivalent in terms of performance (sometimes slightly faster, sometimes slightly slower).

I also considered using TextEncoder/TextDecoder for this, but it was much slower (~85%).
2021-09-27 13:34:39 -04:00
Justin Grant c88fb49d37
Improve DEV errors if string coercion throws (Temporal.*, Symbol, etc.) (#22064)
* Revise ESLint rules for string coercion

Currently, react uses `'' + value` to coerce mixed values to strings.
This code will throw for Temporal objects or symbols.

To make string-coercion safer and to improve user-facing error messages,
This commit adds a new ESLint rule called `safe-string-coercion`.

This rule has two modes: a production mode and a non-production mode.
* If the `isProductionUserAppCode` option is true, then `'' + value`
  coercions are allowed (because they're faster, although they may
  throw) and `String(value)` coercions are disallowed. Exception:
  when building error messages or running DEV-only code in prod
  files, `String()` should be used because it won't throw.
* If the `isProductionUserAppCode` option is false, then `'' + value`
  coercions are disallowed (because they may throw, and in non-prod
  code it's not worth the risk) and `String(value)` are allowed.

Production mode is used for all files which will be bundled with
developers' userland apps. Non-prod mode is used for all other React
code: tests, DEV blocks, devtools extension, etc.

In production mode, in addiiton to flagging `String(value)` calls,
the rule will also flag `'' + value` or `value + ''` coercions that may
throw. The rule is smart enough to silence itself in the following
"will never throw" cases:
* When the coercion is wrapped in a `typeof` test that restricts to safe
  (non-symbol, non-object) types. Example:
    if (typeof value === 'string' || typeof value === 'number') {
      thisWontReport('' + value);
    }
* When what's being coerced is a unary function result, because unary
   functions never return an object or a symbol.
* When the coerced value is a commonly-used numeric identifier:
  `i`, `idx`, or `lineNumber`.
* When the statement immeidately before the coercion is a DEV-only
  call to a function from shared/CheckStringCoercion.js. This call is a
  no-op in production, but in DEV it will show a console error
  explaining the problem, then will throw right after a long explanatory
  code comment so that debugger users will have an idea what's going on.
  The check function call must be in the following format:
    if (__DEV__) {
      checkXxxxxStringCoercion(value);
    };

Manually disabling the rule is usually not necessary because almost all
prod use of the `'' + value` pattern falls into one of the categories
above. But in the rare cases where the rule isn't smart enough to detect
safe usage (e.g. when a coercion is inside a nested ternary operator),
manually disabling the rule will be needed.

The rule should also be manually disabled in prod error handling code
where `String(value)` should be used for coercions, because it'd be
bad to throw while building an error message or stack trace!

The prod and non-prod modes have differentiated error messages to
explain how to do a proper coercion in that mode.

If a production check call is needed but is missing or incorrect
(e.g. not in a DEV block or not immediately before the coercion), then
a context-sensitive error message will be reported so that developers
can figure out what's wrong and how to fix the problem.

Because string coercions are now handled by the `safe-string-coercion`
rule, the `no-primitive-constructor` rule no longer flags `String()`
usage. It still flags `new String(value)` because that usage is almost
always a bug.

* Add DEV-only string coercion check functions

This commit adds DEV-only functions to check whether coercing
values to strings using the `'' + value` pattern will throw. If it will
throw, these functions will:
1. Display a console error with a friendly error message describing
   the problem and the developer can fix it.
2. Perform the coercion, which will throw. Right before the line where
   the throwing happens, there's a long code comment that will help
   debugger users (or others looking at the exception call stack) figure
   out what happened and how to fix the problem.

One of these check functions should be called before all string coercion
of user-provided values, except when the the coercion is guaranteed not
to throw, e.g.
* if inside a typeof check like `if (typeof value === 'string')`
* if coercing the result of a unary function like `+value` or `value++`
* if coercing a variable named in a whitelist of numeric identifiers:
  `i`, `idx`, or `lineNumber`.

The new `safe-string-coercion` internal ESLint rule enforces that
these check functions are called when they are required.

Only use these check functions in production code that will be bundled
with user apps.  For non-prod code (and for production error-handling
code), use `String(value)` instead which may be a little slower but will
never throw.

* Add failing tests for string coercion

Added failing tests to verify:
* That input, select, and textarea elements with value and defaultValue
  set to Temporal-like objects which will throw when coerced to string
  using the `'' + value` pattern.
* That text elements will throw for Temporal-like objects
* That dangerouslySetInnerHTML will *not* throw for Temporal-like
  objects because this value is not cast to a string before passing to
  the DOM.
* That keys that are Temporal-like objects will throw

All tests above validate the friendly error messages thrown.

* Use `String(value)` for coercion in non-prod files

This commit switches non-production code from `'' + value` (which
throws for Temporal objects and symbols) to instead use `String(value)`
which won't throw for these or other future plus-phobic types.

"Non-produciton code" includes anything not bundled into user apps:
* Tests and test utilities. Note that I didn't change legacy React
  test fixtures because I assumed it was good for those files to
  act just like old React, including coercion behavior.
* Build scripts
* Dev tools package - In addition to switching to `String`, I also
  removed special-case code for coercing symbols which is now
  unnecessary.

* Add DEV-only string coercion checks to prod files

This commit adds DEV-only function calls to to check if string coercion
using `'' + value` will throw, which it will if the value is a Temporal
object or a symbol because those types can't be added with `+`.

If it will throw, then in DEV these checks will show a console error
to help the user undertsand what went wrong and how to fix the
problem. After emitting the console error, the check functions will
retry the coercion which will throw with a call stack that's easy (or
at least easier!) to troubleshoot because the exception happens right
after a long comment explaining the issue. So whether the user is in
a debugger, looking at the browser console, or viewing the in-browser
DEV call stack, it should be easy to understand and fix the problem.

In most cases, the safe-string-coercion ESLint rule is smart enough to
detect when a coercion is safe. But in rare cases (e.g. when a coercion
is inside a ternary) this rule will have to be manually disabled.

This commit also switches error-handling code to use `String(value)`
for coercion, because it's bad to crash when you're trying to build
an error message or a call stack!  Because `String()` is usually
disallowed by the `safe-string-coercion` ESLint rule in production
code, the rule must be disabled when `String()` is used.
2021-09-27 10:05:07 -07:00
Andrew Clark 05726d72cc
[Fix] Errors should not "unsuspend" a transition (#22423)
If an error is thrown during a transition where we would have otherwise
suspended without showing a fallback (i.e. during a refresh), we should
still suspend.

The current behavior is that the error will force the fallback to
appear, even if it's completely unrelated to the component that errored,
which breaks the contract of `startTransition`.
2021-09-27 08:44:39 -07:00
Behnam Mohammadi c9d64e5f4c
add hasOwnProperty to devTools backend (#22437) 2021-09-27 08:56:56 -04:00
BIKI DAS 3746eaf985
Packages/React/src/ReactLazy ---> changing -1 to unintialized (#22421) 2021-09-24 13:52:21 -04:00
Andrew Clark 04ccc01d96
Hydration errors should force a client render (#22416)
* Refactor throwException control flow

I'm about to add more branches to the Suspense-related logic in
`throwException`, so before I do, I split some of the steps into
separate functions so that later I can use them in multiple places.

This commit does not change any program behavior, only the control flow
surrounding existing code.

* Hydration errors should force a client render

If something errors during hydration, we should try rendering again
without hydrating.

We'll find the nearest Suspense boundary and force it to client render,
discarding the server-rendered content.
2021-09-24 10:47:19 -07:00
Andrew Clark 029fdcebbf
root.hydrate -> root.isDehydrated (#22420)
I think this naming is a bit clearer. It means the root is currently
showing server rendered content that needs to be hydrated.

A dehydrated root is conceptually the same as what we call dehydrated
Suspense boundary, so this makes the naming of the root align with the
naming of subtrees.
2021-09-24 10:45:46 -07:00
Brian Vaughn 1c73ceed5f
Scheduling Profiler marks should include thrown Errors (#22419) 2021-09-24 12:56:27 -04:00
Brian Vaughn af87f5a83d
Scheduling Profiler marks should include thrown Errors (#22417)
The scheduling profiler markComponentRenderStopped method is supposed to be called when rendering finishes or when a value is thrown (Suspense or Error). Previously we were calling this in a Suspense-only path of `throwException`.

This PR updates the code to handle errors (or non-Thenables) thrown as well.

It also moves the mark logic the work loop `handleError` method, with Suspense/Error agnostic cleanup.
2021-09-24 12:32:37 -04:00
Brian Vaughn b1a1cb1168
DevTools: Lazily parse indexed map sections (#22415)
Indexed maps divide nested source maps into sections, annotated with a line and column offset. Since these sections are JSON and can be quickly parsed, we can easily separate them without doing the heavier base64 and VLQ decoding process. This PR updates our sourcemap parsing code to defer parsing of an indexed map section until we actually need to retrieve mappings from it.
2021-09-24 11:09:42 -04:00
Brian Vaughn a01cdbf813
Update Yarn build-for-devtools script to account for build/build2 changes (#22418) 2021-09-24 10:54:57 -04:00
Sebastian Markbåge d47339ea36
[Fizz] Remove assignID mechanism (#22410)
* Remove pushEmpty

This is only used to support the assignID mechanism.

* Remove assignID mechanism

This effectively isn't used anyway because we always insert a dummy tag
into the fallback.

* Emit the template tag with an ID directly in pending boundaries

This ensures that assigning the ID is deterministic since it's done during
writing.

This also avoids emitting it for client rendered boundaries that start as
client rendered since we never need to refer to them.

* Move lazy ID initialization to the core implementation

We never need an ID before we write a pending boundary. This also ensures
that ID generation is deterministic by moving it to the write phase.

* Simplify the inserted scripts

We can assume that there are no text nodes before the template tag so this
simplifies the script that finds the comment node. It should be the direct
previous child.
2021-09-24 10:22:02 -04:00
Damian Cyntler 1c58cfab95
Update Node.js to latest v14.17.6 LTS (#22401) 2021-09-24 00:30:12 +01:00
Andrew Clark 3a50d95573
Never attach ping listeners in legacy Suspense (#22407)
I noticed a weird branch where we attach a ping listener even in legacy
mode. It's weird because this shouldn't be necessary. Fallbacks always
synchronously commit in legacy mode, so pings never happen. (A "ping" is
when a suspended promise resolves before the fallback has committed.)

It took me a moment to remember why this case exists, but it's related
to React.lazy.

There's a special case where we suspend while reconciling the children
of a Suspense boundary's inner Offscreen wrapper fiber. This happens
when a React.lazy component is a direct child of a Suspense boundary.

Suspense boundaries are implemented as multiple fibers, but they are a
single conceptual unit. The legacy mode behavior where we pretend the
suspended fiber committed as `null` won't work, because in this case the
"suspended" fiber is the inner Offscreen wrapper.

Because the contents of the boundary haven't started rendering yet (i.e.
nothing in the tree has partially rendered) we can switch to the
regular, concurrent mode behavior: mark the boundary with ShouldCapture
and enter the unwind phase.

However, even though we're switching to the concurrent mode behavior, we
don't need to attach a ping listener. So I refactored the logic so that
it doesn't escape back into the regular path.

It's not really a big deal that we attach an unncessary ping listener,
since this case is so unusual. The motivation is not performance related
— it's to make the logic clearer, because I'm about to add another case
where we trigger a Suspense boundary without attaching a ping listener.
2021-09-23 11:07:38 -07:00
Brian Vaughn d174d063d1
DevTools: Hook names optimizations (#22403)
This commit dramatically improves the performance of the hook names feature by replacing the source-map-js integration with custom mapping code built on top of sourcemap-codec. Based on my own benchmarking, this makes parsing 3-4 times faster. (The bulk of these changes are in SourceMapConsumer.js.)

While implementing this code, I also uncovered a problem with the way we were caching source-map metadata that was causing us to potential parse the same source-map multiple times. (I addressed this in a separate commit for easier reviewing. The bulk of these changes are in parseSourceAndMetadata.js.)

Altogether these changes dramatically improve the performance of the hooks parsing code.

One additional thing we could look into if the source-map download still remains a large bottleneck would be to stream it and decode the mappings array while it streams in rather than in one synchronous chunk after the full source-map has been downloaded.
2021-09-22 20:17:57 -04:00
Andrew Clark 95502f7b25
Update CodeSandbox to pull build artifacts from CI (#22400)
Instead of building them from source.

The `download-experimental-build` script polls CI until the build
has finished.
2021-09-22 11:35:56 -07:00
Andrew Clark 51e017c523
Revert "[Old server renderer] Retry error on client (#22399)"
Going to revert this until we figure out error reporting. It looks like
our downstream infra already supports some type of error recovery so
we might not need it here.
2021-09-22 12:32:28 -04:00
Andrew Clark 5a06072780
[Old server renderer] Retry error on client (#22399)
We're still in the process of migrating to the Fizz server renderer. In
the meantime, this makes the error semantics on the old server renderer
match the behavior of the new one: if an error is thrown, it triggers a
Suspense fallback, just as if it suspended (this part was already
implemented). Then the errored tree is retried on the client, where it
may recover and finish successfully.
2021-09-22 08:45:55 -07:00
Andrew Clark 82c8fa90be
Add back useMutableSource temporarily (#22396)
Recoil uses useMutableSource behind a flag. I thought this was fine
because Recoil isn't used in any concurrent roots, so the behavior
would be the same, but it turns out that it is used by concurrent
roots in a few places.

I'm not expecting it to be hard to migrate to useSyncExternalStore, but
to de-risk the change I'm going to roll it out gradually with a flag. In
the meantime, I've added back the useMutableSource API.
2021-09-21 20:38:24 -07:00
Andrew Clark 8fcfdfff7e
Scrape warning messages in CI (#22393)
There's a downstream workflow that runs the `print-warnings` command. We
can make it faster by scraping the warnings in CI and storing the
result as a build artifact.
2021-09-21 19:03:01 -07:00
Andrew Clark ba07042c86
Fix sizebot mistakenly detecting deleted files (#22394) 2021-09-21 18:55:47 -07:00
Andrew Clark d56947eb2c
Increase polling threshold for publish-prereleases (#22392)
The publish-preleases command prints the URL of the publish workflow
so that you can visit the page and follow along.

But it can take a few seconds before the workflow ID is available, after
you create the pipeline. So the script polls the workflow endpoint
until it's available.

The current polling limit is too low so I increased it.

I also updated the error message to provide more info.
2021-09-21 18:43:59 -07:00
Luna Ruan 5b57bc6e31
[Draft] don't patch console during first render (#22308)
Previously, DevTools always overrode the native console to dim or supress StrictMode double logging. It also overrode console.log (in addition to console.error and console.warn). However, this changes the location shown by the browser console, which causes a bad developer experience. There is currently a TC39 proposal that would allow us to extend console without breaking developer experience, but in the meantime this PR changes the StrictMode console override behavior so that we only patch the console during the StrictMode double render so that, during the first render, the location points to developer code rather than our DevTools console code.
2021-09-21 15:00:11 -07:00
Andrew Clark cf07c3df12
Delete all but one `build2` reference (#22391)
This removes all the remaining references to the `build2` directory
except for the CI job that stores the artifacts. We'll keep the
`build2` artifact until downstream scripts are migrated to `build`.
2021-09-21 13:15:41 -07:00
Andrew Clark bb0d069359
[build2 -> build] Local scripts
Update all our local scripts to use `build` instead of `build2`.

There are still downstream scripts that depend on `build2`, though, so
we can't remove it yet.
2021-09-21 15:14:09 -04:00
Andrew Clark 162bb8c978
[build -> build2] Sizebot 2021-09-21 15:07:16 -04:00
Andrew Clark 2375670a91
Delete unused CI jobs
These were replaced by the `build_combined` job.
2021-09-21 15:06:09 -04:00
Andrew Clark f35287dda4 [build2 -> build] -> download-build-artifacts 2021-09-21 13:47:40 -04:00
Andrew Clark e3c9cd9f41
Add COMMIT_SHA to build directory 2021-09-21 13:41:58 -04:00
Andrew Clark 0c81d347b6 Write artifacts to `build` instead of `build2`
Now that all the CI jobs have been migrated to the new build script,
we can start renaming the `build2` directory to `build`.

Since there are lots of scripts that reference `build2`, including
downstream scripts that live outside this repo, I'm going to keep
the `build2` directory around as a copy of `build`.

Then once all the references are updated, I will delete the copy.
2021-09-21 12:23:48 -04:00
Andrew Clark baff3f2005
Move build_devtools_and_process_artifacts (#22388)
This is the last CI job that needs to be migrated to the new workflow.
2021-09-21 08:51:51 -07:00
Andrew Clark 7c6049695f
Move lint job to new, combined CI workflow (#22386)
* Move lint job to new, combined CI workflow

Moves the lint job our new, combined CI workflow.

After this, there is only one job remaining to be migrated. Then we
can delete the old workflow and build script.

* Remove "stable" CI workflow

This workflow is now empty so we can remove it
2021-09-21 08:16:26 -07:00
salazarm 4da03c9fbd
useSyncExternalStore React Native version (#22367) 2021-09-21 11:07:56 -04:00
Bowen 48d475c9ed
correct typos (#22294)
Co-authored-by: Bowen Li <bowen31337@gmail.com>
2021-09-21 11:05:41 -04:00
Andrew Clark fc57432966
Move DOM fixtures test job to main CI workflow (#22385)
Moves the RELEASE_CHANNEL_stable_yarn_test_dom_fixtures job to our new,
combined CI workflow.

After this, there are only two jobs remaining to be migrated. Then we
can delete the old workflow and build script.
2021-09-21 08:00:28 -07:00
Sebastian Silbermann cb6c619c07
Remove Fiber fields that were used for hydrating useMutableSource (#22368) 2021-09-20 21:12:30 +01:00
salazarm 64e70f82e9
[Fizz] add avoidThisFallback support (#22318) 2021-09-20 15:44:48 -04:00
38elements cbf6178ed3
Fix links in packages/react-devtools/README.md (#22356) 2021-09-20 15:10:29 -04:00
Konstantin Popov acf8ada4c0
Fix typo in types.js (react-devtools-shared) (#22299)
Fix typo:

becuase -> because
2021-09-20 15:09:59 -04:00
BIKI DAS c62d5831d3
react-devtools\OVERVIEW.md --> fixed typos (#22372) 2021-09-20 15:09:37 -04:00
Brian Vaughn 7e8fb98e14
Enabled enableProfilerChangedHookIndices feature flag for all builds (#22365) 2021-09-20 13:10:50 -04:00
Sebastian Silbermann 3ee7a004e5
devtools: Display actual ReactDOM API name in root type (#22363) 2021-09-20 11:44:21 -04:00
Andrew Clark 79b8fc6670
Implement getServerSnapshot in userspace shim (#22359)
* Convert useSES shim tests to use React DOM

Idea is that eventually we'll run these tests against an actual build of
React DOM 17 to test backwards compatibility.

* Implement getServerSnapshot in userspace shim

If the DOM is not present, we assume that we are running in a server
environment and return the result of `getServerSnapshot`.

This heuristic doesn't work in React Native, so we'll need to provide
a separate native build (using the `.native` extension). I've left this
for a follow-up.

We can't call `getServerSnapshot` on the client, because in versions of
React before 18, there's no built-in mechanism to detect whether we're
hydrating. To avoid a server mismatch warning, users must account for
this themselves and return the correct value inside `getSnapshot`.

Note that none of this is relevant to the built-in API that is being
added in 18. This only affects the userspace shim that is provided
for backwards compatibility with versions 16 and 17.
2021-09-20 08:32:13 -07:00
Andrew Clark 86b3e2461d
Implement useSyncExternalStore on server (#22347)
Adds a third argument called `getServerSnapshot`.

On the server, React calls this one instead of the normal `getSnapshot`.
We also call it during hydration.

So it represents the snapshot that is used to generate the initial,
server-rendered HTML. The purpose is to avoid server-client mismatches.
What we render during hydration needs to match up exactly with what we
render on the server.

The pattern is for the server to send down a serialized copy of the
store that was used to generate the initial HTML. On the client, React
will call either `getSnapshot` or `getServerSnapshot` on the client as
appropriate, depending on whether it's currently hydrating.

The argument is optional for fully client rendered use cases. If the
user does attempt to omit `getServerSnapshot`, and the hook is called
on the server, React will abort that subtree on the server and
revert to client rendering, up to the nearest Suspense boundary.

For the userspace shim, we will need to use a heuristic (canUseDOM)
to determine whether we are in a server environment. I'll do that in
a follow up.
2021-09-20 08:31:02 -07:00
Brian Vaughn 57e4d6872f
replace-fork: Cleanup after failure if no unstaged changes (#22364) 2021-09-20 11:10:17 -04:00
Andrew Clark 293059e52b
replace-fork should not clear uncommitted changes (#22348)
The replace-fork script depends on ESLint to fix the reconciler imports
— `.old` -> `.new` or vice versa. If ESLint crashes, it can leave the
imports in an incorrect state.

As a convenience, @bvaughn updated the script to automatically run
`git checkout -- .` if the ESLint command fails. An unintended
consequence of the strategy is that if the working directory is not
clean, then any uncommitted changes will be lost.

We need a better strategy for this that prevents the accidental loss of
work. One option is to exit early if the working directory is not clean
before you run the script, though that affects the usability of
the script.

An ideal solution would reset the working directory back to whatever
state it was in before the script ran, perhaps by stashing all the
changes and restoring them if the script aborts.

Until we think of something better, I've commmented out the branch.
2021-09-20 10:07:25 -04:00
Andrew Clark 8209de2695
Delete useMutableSource implementation (#22292)
This API was replaced by useSyncExternalStore
2021-09-19 21:11:50 -07:00
Brian Vaughn f50ff357cf
DevTools: Fix memory leak via alternate Fiber pointer (#22346) 2021-09-17 12:53:07 -04:00
Brian Vaughn bc9bb87c2b
Clear performance marks after measuring (#22345) 2021-09-17 10:46:42 -04:00