Compare commits

..

1 Commits

Author SHA1 Message Date
Sebastian Markbåge
d346d92084 Allow Suspense Mismatch on the Client to Silently Proceed (#16943)
* Regression test: Suspense + hydration + legacy

* Allow Suspense Mismatch on the Client to Silently Proceed

This fixes but isn't actually the semantics that we want this case to have.
2019-09-28 10:50:57 -07:00
735 changed files with 19194 additions and 36601 deletions

View File

@@ -22,32 +22,6 @@ aliases:
- &attach_workspace
at: build
- &process_artifacts
docker: *docker
environment: *environment
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run: node ./scripts/rollup/consolidateBundleSizes.js
- run: ./scripts/circleci/upload_build.sh
- run: ./scripts/circleci/pack_and_store_artifact.sh
- store_artifacts:
path: ./node_modules.tgz
- store_artifacts:
path: ./build.tgz
- store_artifacts:
path: ./build/bundle-sizes.json
- store_artifacts:
# TODO: Update release script to use local file instead of pulling
# from artifacts.
path: ./scripts/error-codes/codes.json
- persist_to_workspace:
root: build
paths:
- bundle-sizes.json
jobs:
setup:
docker: *docker
@@ -98,22 +72,7 @@ jobs:
- checkout
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test --maxWorkers=2
test_source_experimental:
docker: *docker
environment: *environment
steps:
- checkout
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: yarn test --maxWorkers=2
- run: yarn test --maxWorkers=2
test_source_persistent:
docker: *docker
@@ -123,10 +82,7 @@ jobs:
- checkout
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-persistent --maxWorkers=2
- run: yarn test-persistent --maxWorkers=2
test_source_prod:
docker: *docker
@@ -136,10 +92,7 @@ jobs:
- checkout
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-prod --maxWorkers=2
- run: yarn test-prod --maxWorkers=2
build:
docker: *docker
@@ -149,102 +102,40 @@ jobs:
- checkout
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: stable
command: |
./scripts/circleci/add_build_info_json.sh
./scripts/circleci/update_package_versions.sh
yarn build
- run: echo "stable" >> build/RELEASE_CHANNEL
- run: ./scripts/circleci/add_build_info_json.sh
- run: ./scripts/circleci/update_package_versions.sh
- run: yarn build
- persist_to_workspace:
root: build
paths:
- RELEASE_CHANNEL
- facebook-www
- node_modules
- react-native
- dist
- sizes/*.json
build_experimental:
process_artifacts:
docker: *docker
environment: *environment
parallelism: 20
steps:
- checkout
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: |
./scripts/circleci/add_build_info_json.sh
./scripts/circleci/update_package_versions.sh
yarn build
- run: echo "experimental" >> build/RELEASE_CHANNEL
- persist_to_workspace:
root: build
paths:
- RELEASE_CHANNEL
- facebook-www
- node_modules
- react-native
- dist
- sizes/*.json
build_devtools_and_process_artifacts:
docker: *docker
environment: *environment
parallelism: 20
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: ./scripts/circleci/pack_and_store_devtools_artifacts.sh
- run: node ./scripts/rollup/consolidateBundleSizes.js
- run: node ./scripts/tasks/danger
- run: ./scripts/circleci/upload_build.sh
- run: ./scripts/circleci/pack_and_store_artifact.sh
- store_artifacts:
path: ./build/devtools.tgz
# These jobs are named differently so we can distinguish the stable and
# and experimental artifacts
process_artifacts: *process_artifacts
process_artifacts_experimental: *process_artifacts
sizebot:
docker: *docker
environment: *environment
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
# This runs in the process_artifacts job, too, but it's faster to run
# this step in both jobs instead of running the jobs sequentially
- run: node ./scripts/rollup/consolidateBundleSizes.js
- run:
environment:
RELEASE_CHANNEL: stable
command: node ./scripts/tasks/danger
sizebot_experimental:
docker: *docker
environment: *environment
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
# This runs in the process_artifacts job, too, but it's faster to run
# this step in both jobs instead of running the jobs sequentially
- run: node ./scripts/rollup/consolidateBundleSizes.js
- run:
environment:
RELEASE_CHANNEL: experimental
command: node ./scripts/tasks/danger
path: ./node_modules.tgz
- store_artifacts:
path: ./build.tgz
- store_artifacts:
path: ./build/bundle-sizes.json
- store_artifacts:
# TODO: Update release script to use local file instead of pulling
# from artifacts.
path: ./scripts/error-codes/codes.json
lint_build:
docker: *docker
@@ -265,12 +156,9 @@ jobs:
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-build --maxWorkers=2
- run: yarn test-build --maxWorkers=2
test_build_experimental:
test_build_devtools:
docker: *docker
environment: *environment
steps:
@@ -278,23 +166,7 @@ jobs:
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: yarn test-build --maxWorkers=2
test_devtools:
docker: *docker
environment: *environment
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: yarn test-build-devtools --maxWorkers=2
- run: yarn test-build-devtools --maxWorkers=2
test_dom_fixtures:
docker: *docker
@@ -305,8 +177,6 @@ jobs:
- *restore_yarn_cache
- run:
name: Run DOM fixture tests
environment:
RELEASE_CHANNEL: stable
command: |
cd fixtures/dom
yarn --frozen-lockfile
@@ -334,27 +204,11 @@ jobs:
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: stable
command: yarn test-build-prod --maxWorkers=2
test_build_prod_experimental:
docker: *docker
environment: *environment
steps:
- checkout
- attach_workspace: *attach_workspace
- *restore_yarn_cache
- *run_yarn
- run:
environment:
RELEASE_CHANNEL: experimental
command: yarn test-build-prod --maxWorkers=2
- run: yarn test-build-prod --maxWorkers=2
workflows:
version: 2
stable:
commit:
jobs:
- setup
- lint:
@@ -378,9 +232,6 @@ workflows:
- process_artifacts:
requires:
- build
- sizebot:
requires:
- build
- lint_build:
requires:
- build
@@ -390,45 +241,15 @@ workflows:
- test_build_prod:
requires:
- build
- test_build_devtools:
requires:
- build
- test_dom_fixtures:
requires:
- build
experimental:
jobs:
- setup
- test_source_experimental:
requires:
- setup
- build_experimental:
requires:
- setup
- process_artifacts_experimental:
requires:
- build_experimental
- sizebot_experimental:
requires:
- build_experimental
- test_build_experimental:
requires:
- build_experimental
- test_build_prod_experimental:
requires:
- build_experimental
- lint_build:
requires:
- build_experimental
- test_devtools:
requires:
- build_experimental
- build_devtools_and_process_artifacts:
requires:
- build_experimental
fuzz_tests:
hourly:
triggers:
- schedule:
# Fuzz tests run hourly
cron: "0 * * * *"
filters:
branches:

View File

@@ -1,10 +0,0 @@
{
"packages": ["packages/react", "packages/react-dom", "packages/scheduler"],
"buildCommand": "build --type=NODE react/index,react-dom/index,scheduler/index,scheduler/tracing",
"publishDirectory": {
"react": "build/node_modules/react",
"react-dom": "build/node_modules/react-dom",
"scheduler": "build/node_modules/scheduler"
},
"sandboxes": ["new"]
}

View File

@@ -9,10 +9,7 @@ const OFF = 0;
const ERROR = 2;
module.exports = {
extends: [
'fbjs',
'prettier'
],
extends: 'fbjs',
// Stop ESLint from looking for a configuration file in parent folders
root: true,
@@ -33,10 +30,10 @@ module.exports = {
rules: {
'accessor-pairs': OFF,
'brace-style': [ERROR, '1tbs'],
'comma-dangle': [ERROR, 'always-multiline'],
'consistent-return': OFF,
'dot-location': [ERROR, 'property'],
// We use console['error']() as a signal to not transform it:
'dot-notation': [ERROR, {allowPattern: '^(error|warn)$'}],
'dot-notation': ERROR,
'eol-last': ERROR,
eqeqeq: [ERROR, 'allow-null'],
indent: OFF,
@@ -95,9 +92,7 @@ module.exports = {
// the second argument of warning/invariant should be a literal string
'react-internal/no-primitive-constructors': ERROR,
'react-internal/no-to-warn-dev-within-to-throw': ERROR,
'react-internal/invariant-args': ERROR,
'react-internal/warning-args': ERROR,
'react-internal/no-production-logging': ERROR,
'react-internal/warning-and-invariant-args': ERROR,
},
overrides: [
@@ -138,19 +133,6 @@ module.exports = {
'jest/valid-expect-in-promise': ERROR,
},
},
{
files: [
'**/__tests__/**/*.js',
'scripts/**/*.js',
'packages/*/npm/**/*.js',
'packages/dom-event-testing-library/**/*.js',
'packages/react-devtools*/**/*.js'
],
rules: {
'react-internal/no-production-logging': OFF,
'react-internal/warning-args': OFF,
},
},
{
files: ['packages/react-native-renderer/**/*.js'],
globals: {
@@ -167,7 +149,6 @@ module.exports = {
spyOnProd: true,
__PROFILE__: true,
__UMD__: true,
__EXPERIMENTAL__: true,
trustedTypes: true,
},
};

View File

@@ -1,4 +1,14 @@
👉 Please follow one of these issue templates:
- https://github.com/facebook/react/issues/new/choose
<!--
Note: if the issue is about documentation or the website, please file it at:
https://github.com/reactjs/reactjs.org/issues/new
-->
Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates.
**Do you want to request a *feature* or report a *bug*?**
**What is the current behavior?**
**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:**
**What is the expected behavior?**
**Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?**

View File

@@ -1,41 +0,0 @@
---
name: "🐛 Bug Report"
about: Report a reproducible bug or regression.
title: 'Bug: '
labels: 'Status: Unconfirmed'
---
<!--
Please provide a clear and concise description of what the bug is. Include
screenshots if needed. Please test using the latest version of the relevant
React packages to make sure your issue has not already been fixed.
-->
React version:
## Steps To Reproduce
1.
2.
<!--
Your bug will get fixed much faster if we can run your code and it doesn't
have dependencies other than React. Issues without reproduction steps or
code examples may be immediately closed as not actionable.
-->
Link to code example:
<!--
Please provide a CodeSandbox (https://codesandbox.io/s/new), a link to a
repository on GitHub, or provide a minimal code example that reproduces the
problem. You may provide a screenshot of the application if you think it is
relevant to your bug report. Here are some tips for providing a minimal
example: https://stackoverflow.com/help/mcve.
-->
## The current behavior
## The expected behavior

View File

@@ -1,13 +0,0 @@
---
name: "📃 Documentation Issue"
about: This issue tracker is not for documentation issues. Please file documentation issues at https://github.com/reactjs/reactjs.org.
title: 'Docs: '
labels: 'Resolution: Invalid'
---
🚨 This issue tracker is not for documentation issues. 🚨
The React website is hosted on a separate repository. You may let the
team know about any issues with the documentation by opening an issue there:
- https://github.com/reactjs/reactjs.org/issues/new

View File

@@ -1,29 +0,0 @@
---
name: "🤔 Questions and Help"
about: This issue tracker is not for questions. Please ask questions at https://stackoverflow.com/questions/tagged/react.
title: 'Question: '
labels: 'Resolution: Invalid, Type: Question'
---
🚨 This issue tracker is not for questions. 🚨
As it happens, support requests that are created as issues are likely to be closed. We want to make sure you are able to find the help you seek. Please take a look at the following resources.
## Coding Questions
If you have a coding question related to React and React DOM, it might be better suited for Stack Overflow. It's a great place to browse through frequent questions about using React, as well as ask for help with specific questions.
https://stackoverflow.com/questions/tagged/react
## Talk to other React developers
There are many online forums which are a great place for discussion about best practices and application architecture as well as the future of React.
https://reactjs.org/community/support.html#popular-discussion-forums
## Proposals
If you'd like to discuss topics related to the future of React, or would like to propose a new feature or change before sending a pull request, please check out the discussions and proposals repository.
https://github.com/reactjs/rfcs

View File

@@ -1,27 +1,14 @@
<!--
Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory.
**Before submitting a pull request,** please make sure the following is done:
Before submitting a pull request, please make sure the following is done:
1. Fork [the repository](https://github.com/facebook/react) and create your branch from `master`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development.
5. Run `yarn test-prod` to test in the production environment. It supports the same options as `yarn test`.
6. If you need a debugger, run `yarn debug-test --watch TestName`, open `chrome://inspect`, and press "Inspect".
7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files.
9. Run the [Flow](https://flowtype.org/) typechecks (`yarn flow`).
10. If you haven't already, complete the CLA.
1. Fork [the repository](https://github.com/facebook/react) and create your branch from `master`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development.
5. Run `yarn test-prod` to test in the production environment. It supports the same options as `yarn test`.
6. If you need a debugger, run `yarn debug-test --watch TestName`, open `chrome://inspect`, and press "Inspect".
7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files.
9. Run the [Flow](https://flowtype.org/) typechecks (`yarn flow`).
10. If you haven't already, complete the CLA.
Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html
-->
## Summary
<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->
## Test Plan
<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface. -->
**Learn more about contributing:** https://reactjs.org/docs/how-to-contribute.html

31
.github/stale.yml vendored
View File

@@ -1,31 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "Partner"
- "React Core Team"
- "Resolution: Backlog"
- "Type: Bug"
- "Type: Discussion"
- "Type: Regression"
# Label to use when marking an issue as stale
staleLabel: "Resolution: Stale"
issues:
# Comment to post when marking an issue as stale.
markComment: >
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contribution.
# Comment to post when closing a stale issue.
closeComment: >
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!
pulls:
# Comment to post when marking a pull request as stale.
markComment: >
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contribution.
# Comment to post when closing a stale pull request.
closeComment: >
Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you!

View File

@@ -88,7 +88,6 @@ Kevin Coughlin <kevintcoughlin@gmail.com> <kevincoughlin@tumblr.com>
Krystian Karczewski <karcz.k@gmail.com>
Kunal Mehta <k.mehta@berkeley.edu> <kunalm@fb.com>
Laurence Rowe <l@lrowe.co.uk> <laurence@lrowe.co.uk>
Lea Rosema <terabaud@gmail.com>
Marcin K. <katzoo@github.mail>
Mark Anderson <undernewmanagement@users.noreply.github.com>
Mark Funk <mfunk86@gmail.com> <mark@boomtownroi.com>

View File

@@ -8,7 +8,7 @@ module.exports = {
jsxBracketSameLine: true,
trailingComma: 'es5',
printWidth: 80,
parser: 'babel',
parser: 'babylon',
overrides: [
{

View File

@@ -6,56 +6,6 @@
</summary>
</details>
## 16.12.0 (November 14, 2019)
### React DOM
* Fix passive effects (`useEffect`) not being fired in a multi-root app. ([@acdlite](https://github.com/acdlite) in [#17347](https://github.com/facebook/react/pull/17347))
### React Is
* Fix `lazy` and `memo` types considered elements instead of components ([@bvaughn](https://github.com/bvaughn) in [#17278](https://github.com/facebook/react/pull/17278))
## 16.11.0 (October 22, 2019)
### React DOM
* Fix `mouseenter` handlers from firing twice inside nested React containers. [@yuanoook](https://github.com/yuanoook) in [#16928](https://github.com/facebook/react/pull/16928)
* Remove `unstable_createRoot` and `unstable_createSyncRoot` experimental APIs. (These are available in the Experimental channel as `createRoot` and `createSyncRoot`.) ([@acdlite](http://github.com/acdlite) in [#17088](https://github.com/facebook/react/pull/17088))
## 16.10.2 (October 3, 2019)
### React DOM
* Fix regression in react-native-web by restoring order of arguments in event plugin extractors ([@necolas](https://github.com/necolas) in [#16978](https://github.com/facebook/react/pull/16978))
## 16.10.1 (September 28, 2019)
### React DOM
* Fix regression in Next.js apps by allowing Suspense mismatch during hydration to silently proceed ([@sebmarkbage](https://github.com/sebmarkbage) in [#16943](https://github.com/facebook/react/pull/16943))
## 16.10.0 (September 27, 2019)
### React DOM
* Fix edge case where a hook update wasn't being memoized. ([@sebmarkbage](http://github.com/sebmarkbage) in [#16359](https://github.com/facebook/react/pull/16359))
* Fix heuristic for determining when to hydrate, so we don't incorrectly hydrate during an update. ([@sebmarkbage](http://github.com/sebmarkbage) in [#16739](https://github.com/facebook/react/pull/16739))
* Clear additional fiber fields during unmount to save memory. ([@trueadm](http://github.com/trueadm) in [#16807](https://github.com/facebook/react/pull/16807))
* Fix bug with required text fields in Firefox. ([@halvves](http://github.com/halvves) in [#16578](https://github.com/facebook/react/pull/16578))
* Prefer `Object.is` instead of inline polyfill, when available. ([@ku8ar](http://github.com/ku8ar) in [#16212](https://github.com/facebook/react/pull/16212))
* Fix bug when mixing Suspense and error handling. ([@acdlite](http://github.com/acdlite) in [#16801](https://github.com/facebook/react/pull/16801))
### Scheduler (Experimental)
* Improve queue performance by switching its internal data structure to a min binary heap. ([@acdlite](http://github.com/acdlite) in [#16245](https://github.com/facebook/react/pull/16245))
* Use `postMessage` loop with short intervals instead of attempting to align to frame boundaries with `requestAnimationFrame`. ([@acdlite](http://github.com/acdlite) in [#16214](https://github.com/facebook/react/pull/16214))
### useSubscription
* Avoid tearing issue when a mutation happens and the previous update is still in progress. ([@bvaughn](http://github.com/bvaughn) in [#16623](https://github.com/facebook/react/pull/16623))
## 16.9.0 (August 8, 2019)
### React
@@ -534,7 +484,7 @@ This release was published in a broken state and should be skipped.
* Fix an issue with `this.state` of different components getting mixed up. ([@sophiebits](https://github.com/sophiebits) in [#12323](https://github.com/facebook/react/pull/12323))
* Provide a better message when component type is undefined. ([@HeroProtagonist](https://github.com/HeroProtagonist) in [#11966](https://github.com/facebook/react/pull/11966))
### React Test Renderer
## React Test Renderer
* Fix handling of fragments in `toTree()`. ([@maciej-ka](https://github.com/maciej-ka) in [#12107](https://github.com/facebook/react/pull/12107) and [@gaearon](https://github.com/gaearon) in [#12154](https://github.com/facebook/react/pull/12154))
* Shallow renderer should assign state to `null` for components that don't set it. ([@jwbay](https://github.com/jwbay) in [#11965](https://github.com/facebook/react/pull/11965))

View File

@@ -1,7 +0,0 @@
# Reporting Security Issues
If you believe you have found a security vulnerability in React, we encourage you to let us know right away. We will investigate all legitimate reports and do our best to quickly fix the problem.
Please refer to the following page for our responsible disclosure policy, reward guidelines, and those things that should not be reported:
https://www.facebook.com/whitehat

View File

@@ -32,14 +32,6 @@ const {generateResultsArray} = require('./scripts/rollup/stats');
const {existsSync, readFileSync} = require('fs');
const {exec} = require('child_process');
// This must match the name of the CI job that creates the build artifacts
const RELEASE_CHANNEL =
process.env.RELEASE_CHANNEL === 'experimental' ? 'experimental' : 'stable';
const artifactsJobName =
process.env.RELEASE_CHANNEL === 'experimental'
? 'process_artifacts_experimental'
: 'process_artifacts';
if (!existsSync('./build/bundle-sizes.json')) {
// This indicates the build failed previously.
// In that case, there's nothing for the Dangerfile to do.
@@ -125,8 +117,6 @@ function git(args) {
return;
}
markdown(`## Size changes (${RELEASE_CHANNEL})`);
const upstreamRef = danger.github.pr.base.ref;
await git(`remote add upstream https://github.com/facebook/react.git`);
await git('fetch upstream');
@@ -145,7 +135,8 @@ function git(args) {
}
for (let i = 0; i < statuses.length; i++) {
const status = statuses[i];
if (status.context === `ci/circleci: ${artifactsJobName}`) {
// This must match the name of the CI job that creates the build artifacts
if (status.context === 'ci/circleci: process_artifacts') {
if (status.state === 'success') {
baseCIBuildId = /\/facebook\/react\/([0-9]+)/.exec(
status.target_url

View File

@@ -461,7 +461,9 @@ function prepareState(initGlobals) {
hasSameBehaviorForAll,
rowPatternHash,
// "Good enough" id that we can store in localStorage
rowIdHash: `${attribute.name} ${attribute.tagName} ${attribute.overrideStringValue}`,
rowIdHash: `${attribute.name} ${attribute.tagName} ${
attribute.overrideStringValue
}`,
};
const rowGroup = rowPatternHashes.get(rowPatternHash) || new Set();
rowGroup.add(row);
@@ -864,12 +866,14 @@ class App extends React.Component {
// Sort
switch (sortOrder) {
case ALPHABETICAL:
return filteredAttributes.sort((attr1, attr2) =>
attr1.name.toLowerCase() < attr2.name.toLowerCase() ? -1 : 1
return filteredAttributes.sort(
(attr1, attr2) =>
attr1.name.toLowerCase() < attr2.name.toLowerCase() ? -1 : 1
);
case REV_ALPHABETICAL:
return filteredAttributes.sort((attr1, attr2) =>
attr1.name.toLowerCase() < attr2.name.toLowerCase() ? 1 : -1
return filteredAttributes.sort(
(attr1, attr2) =>
attr1.name.toLowerCase() < attr2.name.toLowerCase() ? 1 : -1
);
case GROUPED_BY_ROW_PATTERN: {
return filteredAttributes.sort((attr1, attr2) => {

View File

@@ -268,7 +268,7 @@ async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
async@^1.5.2:
async@^1.4.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@@ -1448,10 +1448,6 @@ commander@2.11.x, commander@~2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
commander@~2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -2842,14 +2838,14 @@ handle-thing@^1.2.5:
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
handlebars@^4.0.3:
version "4.5.3"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
version "4.0.10"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f"
dependencies:
neo-async "^2.6.0"
async "^1.4.0"
optimist "^0.6.1"
source-map "^0.6.1"
source-map "^0.4.4"
optionalDependencies:
uglify-js "^3.1.4"
uglify-js "^2.6"
har-schema@^1.0.5:
version "1.0.5"
@@ -4184,10 +4180,6 @@ negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
neo-async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
no-case@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081"
@@ -5704,9 +5696,11 @@ source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, sourc
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
amdefine ">=0.0.4"
source-map@~0.2.0:
version "0.2.0"
@@ -6096,7 +6090,7 @@ uglify-js@3.0.x, uglify-js@^3.0.13:
commander "~2.11.0"
source-map "~0.5.1"
uglify-js@^2.8.29:
uglify-js@^2.6, uglify-js@^2.8.29:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
dependencies:
@@ -6105,13 +6099,6 @@ uglify-js@^2.8.29:
optionalDependencies:
uglify-to-browserify "~1.0.0"
uglify-js@^3.1.4:
version "3.7.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
dependencies:
commander "~2.20.3"
source-map "~0.6.1"
uglify-to-browserify@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"

View File

@@ -17,7 +17,6 @@ let TestRenderer;
let ARTTest;
global.__DEV__ = process.env.NODE_ENV !== 'production';
global.__EXPERIMENTAL__ = process.env.RELEASE_CHANNEL === 'experimental';
expect.extend(require('../toWarnDev'));
@@ -177,21 +176,19 @@ it("doesn't warn if you use nested acts from different renderers", () => {
});
});
if (__EXPERIMENTAL__) {
it('warns when using createRoot() + .render', () => {
const root = ReactDOM.createRoot(document.createElement('div'));
expect(() => {
TestRenderer.act(() => {
root.render(<App />);
});
}).toWarnDev(
[
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked',
"It looks like you're using the wrong act()",
],
{
withoutStack: true,
}
);
});
}
it('warns when using createRoot() + .render', () => {
const root = ReactDOM.unstable_createRoot(document.createElement('div'));
expect(() => {
TestRenderer.act(() => {
root.render(<App />);
});
}).toWarnDev(
[
'In Concurrent or Sync modes, the "scheduler" module needs to be mocked',
"It looks like you're using the wrong act()",
],
{
withoutStack: true,
}
);
});

View File

@@ -55,4 +55,4 @@ class IframeSubtree extends React.Component {
}
}
export default ReactDOM.createPortal ? IframePortal : IframeSubtree;
export default (ReactDOM.createPortal ? IframePortal : IframeSubtree);

View File

@@ -13,7 +13,7 @@ export default class ButtonTestCases extends React.Component {
<FixtureSet title="Buttons">
<TestCase
title="onClick with disabled buttons"
description="The onClick event handler should not be invoked when clicking on a disabled button">
description="The onClick event handler should not be invoked when clicking on a disabled buyaton">
<TestCase.Steps>
<li>Click on the disabled button</li>
</TestCase.Steps>

View File

@@ -33,8 +33,7 @@ export default function Home() {
<td>
<a href="https://www.mozilla.org/en-US/firefox/organizations/">
ESR<sup></sup>
</a>
, Latest
</a>, Latest
</td>
</tr>
<tr>

View File

@@ -1,6 +1,5 @@
import FixtureSet from '../../FixtureSet';
import MouseMovement from './mouse-movement';
import MouseEnter from './mouse-enter';
const React = window.React;
@@ -9,7 +8,6 @@ class MouseEvents extends React.Component {
return (
<FixtureSet title="Mouse Events">
<MouseMovement />
<MouseEnter />
</FixtureSet>
);
}

View File

@@ -1,73 +0,0 @@
import TestCase from '../../TestCase';
const React = window.React;
const ReactDOM = window.ReactDOM;
const MouseEnter = () => {
const containerRef = React.useRef();
React.useEffect(function() {
const hostEl = containerRef.current;
ReactDOM.render(<MouseEnterDetect />, hostEl, () => {
ReactDOM.render(<MouseEnterDetect />, hostEl.childNodes[1]);
});
}, []);
return (
<TestCase
title="Mouse Enter"
description=""
affectedBrowsers="Chrome, Safari, Firefox">
<TestCase.Steps>
<li>Mouse enter the boxes below, from different borders</li>
</TestCase.Steps>
<TestCase.ExpectedResult>
Mouse enter call count should equal to 1; <br />
Issue{' '}
<a
rel="noopener noreferrer"
target="_blank"
href="https://github.com/facebook/react/issues/16763">
#16763
</a>{' '}
should not happen.
<br />
</TestCase.ExpectedResult>
<div ref={containerRef} />
</TestCase>
);
};
const MouseEnterDetect = () => {
const [log, setLog] = React.useState({});
const firstEl = React.useRef();
const siblingEl = React.useRef();
const onMouseEnter = e => {
const timeStamp = e.timeStamp;
setLog(log => {
const callCount = 1 + (log.timeStamp === timeStamp ? log.callCount : 0);
return {
timeStamp,
callCount,
};
});
};
return (
<React.Fragment>
<div
ref={firstEl}
onMouseEnter={onMouseEnter}
style={{
border: '1px solid #d9d9d9',
padding: '20px 20px',
}}>
Mouse enter call count: {log.callCount || ''}
</div>
<div ref={siblingEl} />
</React.Fragment>
);
};
export default MouseEnter;

View File

@@ -172,7 +172,7 @@ class SelectFixture extends React.Component {
<option value="monkey">monkey</option>
<option value="lion">lion</option>
<option value="mongoose">mongoose</option>
<option value="tiger">tiger</option>
<option value="tiger">tiget</option>
</select>
</form>
</div>

View File

@@ -213,8 +213,8 @@ class TextInputFixtures extends React.Component {
<Fixture>
<SuspendyTree>
<img src="https://upload.wikimedia.org/wikipedia/commons/e/ee/Atom_%282%29.png" />
React is cool
<img src="https://upload.wikimedia.org/wikipedia/commons/e/ee/Atom_%282%29.png" />React
is cool
</SuspendyTree>
</Fixture>
</TestCase>
@@ -308,9 +308,7 @@ class TextInputFixtures extends React.Component {
<div style={{height: 200, overflow: 'scroll'}}>
{Array(20)
.fill()
.map((_, i) => (
<h2 key={i}>{i + 1}</h2>
))}
.map((_, i) => <h2 key={i}>{i + 1}</h2>)}
</div>
</SuspendyTree>
</Fixture>

View File

@@ -205,7 +205,9 @@ const createMatcherFor = consoleMethod =>
if (withoutStack !== warningsWithoutComponentStack.length) {
return {
message: () =>
`Expected ${withoutStack} warnings without a component stack but received ${warningsWithoutComponentStack.length}:\n` +
`Expected ${withoutStack} warnings without a component stack but received ${
warningsWithoutComponentStack.length
}:\n` +
warningsWithoutComponentStack.map(warning =>
this.utils.printReceived(warning)
),

View File

@@ -338,16 +338,26 @@ async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
async@^1.5.2:
async@^1.4.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
async@^2.1.2, async@^2.1.4, async@^2.4.1:
async@^2.1.2, async@^2.4.1:
version "2.5.0"
resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d"
dependencies:
lodash "^4.14.0"
async@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4"
dependencies:
lodash "^4.14.0"
async@~0.2.6:
version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1672,11 +1682,6 @@ commander@^2.9.0:
dependencies:
graceful-readlink ">= 1.0.0"
commander@~2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -2718,7 +2723,7 @@ faye-websocket@~0.11.0:
fb-watchman@^1.8.0:
version "1.9.0"
resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.9.0.tgz#6f268f1f347a6b3c875d1e89da7e1ed79adfc0ec"
resolved "http://registry.npmjs.org/fb-watchman/-/fb-watchman-1.9.0.tgz#6f268f1f347a6b3c875d1e89da7e1ed79adfc0ec"
dependencies:
bser "^1.0.2"
@@ -3136,15 +3141,14 @@ handle-thing@^1.2.5:
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
handlebars@^4.0.3:
version "4.5.3"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==
version "4.0.6"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7"
dependencies:
neo-async "^2.6.0"
async "^1.4.0"
optimist "^0.6.1"
source-map "^0.6.1"
source-map "^0.4.4"
optionalDependencies:
uglify-js "^3.1.4"
uglify-js "^2.6"
har-schema@^1.0.5:
version "1.0.5"
@@ -3593,7 +3597,7 @@ is-plain-obj@^1.0.0:
is-posix-bracket@^0.1.0:
version "0.1.1"
resolved "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
resolved "http://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
is-primitive@^2.0.0:
version "2.0.0"
@@ -4457,7 +4461,7 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8:
minimist@0.0.8, minimist@~0.0.1:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -4465,11 +4469,6 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -4521,11 +4520,6 @@ negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
neo-async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
no-case@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.0.tgz#ca2825ccb76b18e6f79d573dcfbf1eace33dd164"
@@ -4781,7 +4775,6 @@ opn@5.1.0:
optimist@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY=
dependencies:
minimist "~0.0.1"
wordwrap "~0.0.2"
@@ -6153,10 +6146,11 @@ source-map@0.5.6, source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
amdefine ">=0.0.4"
source-map@~0.2.0:
version "0.2.0"
@@ -6586,6 +6580,15 @@ uglify-js@3.0.x, uglify-js@^3.0.13:
commander "~2.11.0"
source-map "~0.5.1"
uglify-js@^2.6:
version "2.7.5"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8"
dependencies:
async "~0.2.6"
source-map "~0.5.1"
uglify-to-browserify "~1.0.0"
yargs "~3.10.0"
uglify-js@^2.8.29:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
@@ -6595,14 +6598,6 @@ uglify-js@^2.8.29:
optionalDependencies:
uglify-to-browserify "~1.0.0"
uglify-js@^3.1.4:
version "3.7.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==
dependencies:
commander "~2.20.3"
source-map "~0.6.1"
uglify-to-browserify@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
@@ -6946,7 +6941,6 @@ wordwrap@0.0.2:
wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
wordwrap@~1.0.0:
version "1.0.0"

View File

@@ -8,19 +8,22 @@ function Comment({comment, commentSource}) {
const currentUserID = comment.viewer.id;
const environment = RelayEnvironment.forUser(currentUserID);
const commentID = nullthrows(comment.id);
useEffect(() => {
const subscription = SubscriptionCounter.subscribeOnce(
`StoreSubscription_${commentID}`,
() =>
StoreSubscription.subscribe(
environment,
{
comment_id: commentID,
},
currentUserID,
commentSource
)
);
return () => subscription.dispose();
}, [commentID, commentSource, currentUserID, environment]);
useEffect(
() => {
const subscription = SubscriptionCounter.subscribeOnce(
`StoreSubscription_${commentID}`,
() =>
StoreSubscription.subscribe(
environment,
{
comment_id: commentID,
},
currentUserID,
commentSource
)
);
return () => subscription.dispose();
},
[commentID, commentSource, currentUserID, environment]
);
}

View File

@@ -83,6 +83,10 @@ alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
ansi-align@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-1.1.0.tgz#2f0c1658829739add5ebb15e6b0c6e3423f016ba"
@@ -256,7 +260,7 @@ async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
async@^1.5.2:
async@^1.4.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@@ -1415,10 +1419,6 @@ commander@2.11.x, commander@~2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
commander@~2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -2818,14 +2818,14 @@ handle-thing@^1.2.5:
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
handlebars@^4.0.3:
version "4.5.3"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
version "4.0.11"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
dependencies:
neo-async "^2.6.0"
async "^1.4.0"
optimist "^0.6.1"
source-map "^0.6.1"
source-map "^0.4.4"
optionalDependencies:
uglify-js "^3.1.4"
uglify-js "^2.6"
har-schema@^1.0.5:
version "1.0.5"
@@ -4179,10 +4179,6 @@ negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
neo-async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
no-case@^2.2.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
@@ -5727,6 +5723,12 @@ source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, sourc
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
amdefine ">=0.0.4"
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
@@ -6117,7 +6119,7 @@ uglify-js@3.1.x, uglify-js@^3.0.13:
commander "~2.11.0"
source-map "~0.6.1"
uglify-js@^2.8.29:
uglify-js@^2.6, uglify-js@^2.8.29:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
dependencies:
@@ -6126,13 +6128,6 @@ uglify-js@^2.8.29:
optionalDependencies:
uglify-to-browserify "~1.0.0"
uglify-js@^3.1.4:
version "3.7.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
dependencies:
commander "~2.20.3"
source-map "~0.6.1"
uglify-to-browserify@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"

View File

@@ -192,7 +192,8 @@ class App extends Component {
}
/>
<p>
Step {currentStep}: {friendlyAction} (
Step {currentStep}
: {friendlyAction} (
<a style={{color: 'gray'}} onClick={this.handleEdit} href="#">
Edit
</a>

View File

@@ -330,76 +330,83 @@ export default function Fibers({fibers, show, graphSettings, ...rest}) {
]}
</div>
</Vertex>,
fiber.child && show.child && (
<Edge
source={fiber.id}
target={fiber.child}
kind="child"
weight={1000}
key={`${fiber.id}-${fiber.child}-child`}>
child
</Edge>
),
fiber.sibling && show.sibling && (
<Edge
source={fiber.id}
target={fiber.sibling}
kind="sibling"
weight={2000}
key={`${fiber.id}-${fiber.sibling}-sibling`}>
sibling
</Edge>
),
fiber.return && show.return && (
<Edge
source={fiber.id}
target={fiber.return}
kind="return"
weight={1000}
key={`${fiber.id}-${fiber.return}-return`}>
return
</Edge>
),
fiber.nextEffect && show.fx && (
<Edge
source={fiber.id}
target={fiber.nextEffect}
kind="fx"
weight={100}
key={`${fiber.id}-${fiber.nextEffect}-nextEffect`}>
nextFx
</Edge>
),
fiber.firstEffect && show.fx && (
<Edge
source={fiber.id}
target={fiber.firstEffect}
kind="fx"
weight={100}
key={`${fiber.id}-${fiber.firstEffect}-firstEffect`}>
firstFx
</Edge>
),
fiber.lastEffect && show.fx && (
<Edge
source={fiber.id}
target={fiber.lastEffect}
kind="fx"
weight={100}
key={`${fiber.id}-${fiber.lastEffect}-lastEffect`}>
lastFx
</Edge>
),
fiber.alternate && show.alt && (
<Edge
source={fiber.id}
target={fiber.alternate}
kind="alt"
weight={10}
key={`${fiber.id}-${fiber.alternate}-alt`}>
alt
</Edge>
),
fiber.child &&
show.child && (
<Edge
source={fiber.id}
target={fiber.child}
kind="child"
weight={1000}
key={`${fiber.id}-${fiber.child}-child`}>
child
</Edge>
),
fiber.sibling &&
show.sibling && (
<Edge
source={fiber.id}
target={fiber.sibling}
kind="sibling"
weight={2000}
key={`${fiber.id}-${fiber.sibling}-sibling`}>
sibling
</Edge>
),
fiber.return &&
show.return && (
<Edge
source={fiber.id}
target={fiber.return}
kind="return"
weight={1000}
key={`${fiber.id}-${fiber.return}-return`}>
return
</Edge>
),
fiber.nextEffect &&
show.fx && (
<Edge
source={fiber.id}
target={fiber.nextEffect}
kind="fx"
weight={100}
key={`${fiber.id}-${fiber.nextEffect}-nextEffect`}>
nextFx
</Edge>
),
fiber.firstEffect &&
show.fx && (
<Edge
source={fiber.id}
target={fiber.firstEffect}
kind="fx"
weight={100}
key={`${fiber.id}-${fiber.firstEffect}-firstEffect`}>
firstFx
</Edge>
),
fiber.lastEffect &&
show.fx && (
<Edge
source={fiber.id}
target={fiber.lastEffect}
kind="fx"
weight={100}
key={`${fiber.id}-${fiber.lastEffect}-lastEffect`}>
lastFx
</Edge>
),
fiber.alternate &&
show.alt && (
<Edge
source={fiber.id}
target={fiber.alternate}
kind="alt"
weight={10}
key={`${fiber.id}-${fiber.alternate}-alt`}>
alt
</Edge>
),
])}
</Graph>
</div>

View File

@@ -194,7 +194,7 @@ async@^0.9.0:
version "0.9.2"
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
async@^1.3.0, async@^1.4.2, async@^1.5.0:
async@^1.3.0, async@^1.4.0, async@^1.4.2, async@^1.5.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@@ -1236,10 +1236,6 @@ commander@2.9.x, commander@^2.9.0:
dependencies:
graceful-readlink ">= 1.0.0"
commander@~2.20.3:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -2306,14 +2302,14 @@ gzip-size@3.0.0:
duplexer "^0.1.1"
handlebars@^4.0.3:
version "4.5.3"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
version "4.0.6"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7"
dependencies:
neo-async "^2.6.0"
async "^1.4.0"
optimist "^0.6.1"
source-map "^0.6.1"
source-map "^0.4.4"
optionalDependencies:
uglify-js "^3.1.4"
uglify-js "^2.6"
har-validator@~2.0.6:
version "2.0.6"
@@ -3373,7 +3369,7 @@ minimatch@3.0.3, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
dependencies:
brace-expansion "^1.0.0"
minimist@0.0.8:
minimist@0.0.8, minimist@~0.0.1:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -3381,10 +3377,6 @@ minimist@^1.1.1, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -3421,10 +3413,6 @@ negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
neo-async@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
no-case@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081"
@@ -4662,7 +4650,7 @@ source-map-support@^0.4.2:
dependencies:
source-map "^0.5.3"
source-map@0.4.x, source-map@~0.4.1:
source-map@0.4.x, source-map@^0.4.4, source-map@~0.4.1:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
@@ -4672,10 +4660,6 @@ source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, sour
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
source-map@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
@@ -4968,7 +4952,7 @@ ua-parser-js@^0.7.9:
version "0.7.12"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb"
uglify-js@2.7.x, uglify-js@~2.7.3:
uglify-js@2.7.x, uglify-js@^2.6, uglify-js@~2.7.3:
version "2.7.5"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8"
dependencies:
@@ -4977,13 +4961,6 @@ uglify-js@2.7.x, uglify-js@~2.7.3:
uglify-to-browserify "~1.0.0"
yargs "~3.10.0"
uglify-js@^3.1.4:
version "3.7.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a"
dependencies:
commander "~2.20.3"
source-map "~0.6.1"
uglify-to-browserify@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"

View File

@@ -1,108 +0,0 @@
<!DOCTYPE html>
<html style="width: 100%; height: 100%; overflow: hidden">
<head>
<meta charset="utf-8">
<title>Flight Example</title>
</head>
<body>
<h1>Flight Example</h1>
<div id="container">
<p>
To install React, follow the instructions on
<a href="https://github.com/facebook/react/">GitHub</a>.
</p>
<p>
If you can see this, React is <strong>not</strong> working right.
If you checked out the source from GitHub make sure to run <code>npm run build</code>.
</p>
</div>
<script src="../../build/dist/react.development.js"></script>
<script src="../../build/dist/react-dom.development.js"></script>
<script src="../../build/dist/react-dom-server.browser.development.js"></script>
<script src="../../build/dist/react-flight-dom-webpack-server.browser.development.js"></script>
<script src="../../build/dist/react-flight-dom-webpack.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
<script type="text/babel">
let Suspense = React.Suspense;
function Text({children}) {
return <span>{children}</span>;
}
function HTML() {
return (
<div>
<Text>hello</Text>
<Text>world</Text>
</div>
);
}
let resolved = false;
let promise = new Promise(resolve => {
setTimeout(() => {
resolved = true;
resolve();
}, 100);
});
function read() {
if (!resolved) {
throw promise;
}
}
function Title() {
read();
return 'Title';
}
let model = {
title: <Title />,
content: {
__html: <HTML />,
}
};
let stream = ReactFlightDOMServer.renderToReadableStream(model);
let response = new Response(stream, {
headers: {'Content-Type': 'text/html'},
});
display(response);
async function display(responseToDisplay) {
let blob = await responseToDisplay.blob();
let url = URL.createObjectURL(blob);
let data = ReactFlightDOMClient.readFromFetch(
fetch(url)
);
// The client also supports XHR streaming.
// var xhr = new XMLHttpRequest();
// xhr.open('GET', url);
// let data = ReactFlightDOMClient.readFromXHR(xhr);
// xhr.send();
renderResult(data);
}
function Shell({ data }) {
let model = data.model;
return <div>
<Suspense fallback="...">
<h1>{model.title}</h1>
</Suspense>
<div dangerouslySetInnerHTML={model.content} />
</div>;
}
function renderResult(data) {
let container = document.getElementById('container');
ReactDOM.render(
<Suspense fallback="Loading...">
<Shell data={data} />
</Suspense>,
container
);
}
</script>
</body>
</html>

View File

@@ -1 +0,0 @@
SKIP_PREFLIGHT_CHECK=true

View File

@@ -1,23 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -1,37 +0,0 @@
{
"name": "flight",
"version": "0.1.0",
"private": true,
"dependencies": {
"@babel/register": "^7.7.0",
"concurrently": "^5.0.0",
"express": "^4.17.1",
"react-scripts": "3.2.0"
},
"scripts": {
"prestart": "cp -r ../../build/node_modules/* ./node_modules/",
"prebuild": "cp -r ../../build/node_modules/* ./node_modules/",
"start": "concurrently \"npm run start:server\" \"npm run start:client\"",
"start:client": "react-scripts start",
"start:server": "NODE_ENV=development node server",
"start:prod": "react-scripts build && NODE_ENV=production node server",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Flight</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@@ -1,28 +0,0 @@
'use strict';
const ReactFlightDOMServer = require('react-flight-dom-webpack/server');
const React = require('react');
const Stream = require('stream');
function Text({children}) {
return <span>{children}</span>;
}
function HTML() {
return (
<div>
<Text>Hello</Text>
<Text>world</Text>
</div>
);
}
module.exports = function(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
let model = {
content: {
__html: <HTML />,
},
};
ReactFlightDOMServer.pipeToNodeWritable(model, res);
};

View File

@@ -1,46 +0,0 @@
'use strict';
const babelRegister = require('@babel/register');
babelRegister({
ignore: [/\/(build|node_modules)\//],
presets: ['react-app'],
});
const express = require('express');
const app = express();
// Application
app.get('/', function(req, res) {
if (process.env.NODE_ENV === 'development') {
for (var key in require.cache) {
delete require.cache[key];
}
}
require('./handler')(req, res);
});
app.listen(3001, () => {
console.log('Flight Server listening on port 3001...');
});
app.on('error', function(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
});

View File

@@ -1,15 +0,0 @@
import React, {Suspense} from 'react';
function Content({data}) {
return <p dangerouslySetInnerHTML={data.model.content} />;
}
function App({data}) {
return (
<Suspense fallback={<h1>Loading...</h1>}>
<Content data={data} />
</Suspense>
);
}
export default App;

View File

@@ -1,7 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import ReactFlightDOMClient from 'react-flight-dom-webpack';
import App from './App';
let data = ReactFlightDOMClient.readFromFetch(fetch('http://localhost:3001'));
ReactDOM.render(<App data={data} />, document.getElementById('root'));

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import React from 'react';
import {createRoot} from 'react-dom';
import {unstable_createRoot} from 'react-dom';
import App from './components/App';
let root = createRoot(document, {hydrate: true});
let root = unstable_createRoot(document, {hydrate: true});
root.render(<App assets={window.assetManifest} />);

View File

@@ -2999,11 +2999,6 @@ js-tokens@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@~3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
@@ -3253,13 +3248,6 @@ loose-envify@^1.0.0:
dependencies:
js-tokens "^3.0.0"
loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
lower-case@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
@@ -3570,10 +3558,9 @@ oauth-sign@~0.8.1:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object.omit@^2.0.0:
version "2.0.1"
@@ -4084,15 +4071,6 @@ promise@7.1.1:
dependencies:
asap "~2.0.3"
prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"
proxy-addr@~1.1.3:
version "1.1.4"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3"
@@ -4182,11 +4160,6 @@ react-dev-utils@^0.5.2:
version "0.0.0"
uid ""
react-is@^16.8.1:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
react-scripts@0.9.5:
version "0.9.5"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-0.9.5.tgz#e9f05c8427e27131662a9b9d7a9786d1ff16bb3f"
@@ -4522,14 +4495,6 @@ sax@^1.2.1, sax@~1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
scheduler@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4"
integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"

View File

@@ -1,5 +1,5 @@
const {createElement, Component, Suspense} = React;
const {createRoot} = ReactDOM;
const {createElement, Component, Placeholder} = React;
const {unstable_createRoot: createRoot} = ReactDOM;
const {
unstable_subscribe: subscribe,
unstable_trace: trace,
@@ -56,8 +56,8 @@ const read = key => {
const TestApp = () =>
createElement(
Suspense,
{fallback: createElement(PlaceholderText)},
Placeholder,
{delayMs: 100, fallback: createElement(PlaceholderText)},
createElement(SuspendingChild, {text: 'foo'}),
createElement(SuspendingChild, {text: 'bar'}),
createElement(SuspendingChild, {text: 'baz'})
@@ -91,11 +91,13 @@ subscribe({
const element = document.getElementById('root');
trace('initial_render', performance.now(), () => {
const root = createRoot(element);
log.app('render()');
root.render(
createElement(TestApp),
const batch = root.createBatch();
log.app('batch.render()');
batch.render(createElement(TestApp));
batch.then(
wrap(() => {
log.app('committed');
log.app('batch.commit()');
batch.commit();
})
);
});

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,5 +1,5 @@
import React, {PureComponent} from 'react';
import {flushSync, createRoot} from 'react-dom';
import {flushSync, unstable_createRoot} from 'react-dom';
import Scheduler from 'scheduler';
import _ from 'lodash';
import Charts from './Charts';
@@ -107,10 +107,9 @@ class App extends PureComponent {
this.debouncedHandleChange(value);
break;
case 'async':
// TODO: useTransition hook instead.
setTimeout(() => {
unstable_scheduleCallback(() => {
this.setState({value});
}, 0);
});
break;
default:
break;
@@ -147,5 +146,5 @@ class App extends PureComponent {
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App />);
const root = ReactDOM.unstable_createRoot(container);
root.render(<App />, container);

View File

@@ -8,7 +8,6 @@
"@babel/code-frame": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/helper-module-imports": "^7.0.0",
"@babel/parser": "^7.0.0",
"@babel/plugin-external-helpers": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
@@ -35,8 +34,9 @@
"@babel/traverse": "^7.0.0",
"@mattiasbuelens/web-streams-polyfill": "0.1.0",
"art": "^0.10.1",
"babel-eslint": "^10.0.3",
"babel-eslint": "^10.0.0",
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
"babylon": "6.18.0",
"chalk": "^1.1.3",
"cli-table": "^0.3.1",
"coffee-script": "^1.8.0",
@@ -46,9 +46,8 @@
"cross-env": "^5.1.1",
"danger": "^9.1.8",
"error-stack-parser": "^2.0.2",
"eslint": "^6.8.0",
"eslint": "^6.1.0",
"eslint-config-fbjs": "^1.1.1",
"eslint-config-prettier": "^6.9.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-flowtype": "^2.25.0",
"eslint-plugin-jest": "^22.15.0",
@@ -57,22 +56,21 @@
"eslint-plugin-react-internal": "link:./scripts/eslint-rules",
"fbjs-scripts": "^0.8.3",
"filesize": "^3.5.6",
"flow-bin": "^0.84.0",
"flow-bin": "^0.72.0",
"glob": "^6.0.4",
"glob-stream": "^6.1.0",
"google-closure-compiler": "20190301.0.0",
"gzip-size": "^3.0.0",
"jasmine-check": "^1.0.0-rc.0",
"jest": "^24.9.0",
"jest-diff": "^24.9.0",
"jest": "^23",
"jest-diff": "^23",
"jest-snapshot-serializer-raw": "^1.1.0",
"minimatch": "^3.0.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"ncp": "^2.0.0",
"object-assign": "^4.1.1",
"pacote": "^9.5.6",
"prettier": "1.19.1",
"prettier": "1.13.7",
"prop-types": "^15.6.2",
"random-seed": "^0.3.0",
"react-lifecycles-compat": "^3.0.2",
@@ -88,33 +86,31 @@
"targz": "^1.0.1",
"through2": "^2.0.0",
"tmp": "~0.0.28",
"typescript": "^3.7.4",
"webpack": "^4.41.2"
"typescript": "~1.8.10"
},
"devEngines": {
"node": "8.x || 9.x || 10.x || 11.x || 12.x || 13.x"
"node": "8.x || 9.x || 10.x || 11.x || 12.x"
},
"jest": {
"testRegex": "/scripts/jest/dont-run-jest-directly\\.js$"
},
"scripts": {
"build": "node ./scripts/rollup/build.js",
"build-for-devtools": "cross-env RELEASE_CHANNEL=experimental yarn build -- react/index,react-dom,react-is,react-debug-tools,scheduler,react-test-renderer --type=NODE",
"linc": "node ./scripts/tasks/linc.js",
"lint": "node ./scripts/tasks/eslint.js",
"lint-build": "node ./scripts/rollup/validate/index.js",
"extract-errors": "yarn build --type=dev --extract-errors",
"postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json && node ./scripts/flow/createFlowConfigs.js && node ./scripts/yarn/downloadReactIsForPrettyFormat.js",
"debug-test": "cross-env NODE_ENV=development node --inspect-brk node_modules/jest/bin/jest.js --config ./scripts/jest/config.source.js --runInBand",
"postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json && node ./scripts/flow/createFlowConfigs.js",
"debug-test": "cross-env NODE_ENV=development node --inspect-brk node_modules/.bin/jest --config ./scripts/jest/config.source.js --runInBand",
"test": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source.js",
"test-persistent": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.source-persistent.js",
"debug-test-persistent": "cross-env NODE_ENV=development node --inspect-brk node_modules/jest/bin/jest.js --config ./scripts/jest/config.source-persistent.js --runInBand",
"debug-test-persistent": "cross-env NODE_ENV=development node --inspect-brk node_modules/.bin/jest --config ./scripts/jest/config.source-persistent.js --runInBand",
"test-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.source.js",
"test-prod-build": "yarn test-build-prod",
"test-build": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.build.js",
"test-build-prod": "cross-env NODE_ENV=production jest --config ./scripts/jest/config.build.js",
"test-build-devtools": "cross-env NODE_ENV=development jest --config ./scripts/jest/config.build-devtools.js",
"debug-test-build-devtools": "cross-env NODE_ENV=development node --inspect-brk node_modules/jest/bin/jest.js --config ./scripts/jest/config.build-devtools.js",
"debug-test-build-devtools": "cross-env NODE_ENV=development node --inspect-brk node_modules/.bin/jest --config ./scripts/jest/config.build-devtools.js",
"test-dom-fixture": "cd fixtures/dom && yarn && yarn prestart && yarn test",
"flow": "node ./scripts/tasks/flow.js",
"flow-ci": "node ./scripts/tasks/flow-ci.js",

View File

@@ -480,25 +480,4 @@ describe('transform react to jsx', () => {
transform(`<Component y={2} {...x} />`, {useBuiltIns: false})
).toMatchSnapshot();
});
it('duplicate children prop should transform into sequence expression with actual children', () => {
expect(
transform(`<Component children={1}>2</Component>`)
).toMatchSnapshot();
});
it('duplicate children prop should transform into sequence expression with next prop', () => {
expect(
transform(`<Component children={1} foo={3}>2</Component>`)
).toMatchSnapshot();
});
it('duplicate children props should transform into sequence expression with next prop', () => {
expect(
transform(`<Component children={1} children={4} foo={3}>2</Component>`)
).toMatchSnapshot();
});
it('duplicate children prop should transform into sequence expression with spread', () => {
expect(
transform(`<Component children={1} {...x}>2</Component>`)
).toMatchSnapshot();
});
});

View File

@@ -46,32 +46,6 @@ var x = React.jsxs("div", {
});
`;
exports[`transform react to jsx duplicate children prop should transform into sequence expression with actual children 1`] = `
React.jsx(Component, {
children: (1, "2")
});
`;
exports[`transform react to jsx duplicate children prop should transform into sequence expression with next prop 1`] = `
React.jsx(Component, {
foo: (1, 3),
children: "2"
});
`;
exports[`transform react to jsx duplicate children prop should transform into sequence expression with spread 1`] = `
React.jsx(Component, Object.assign({}, (1, x), {
children: "2"
}));
`;
exports[`transform react to jsx duplicate children props should transform into sequence expression with next prop 1`] = `
React.jsx(Component, {
foo: (1, 4, 3),
children: "2"
});
`;
exports[`transform react to jsx fragment with no children 1`] = `var x = React.jsx(React.Fragment, {});`;
exports[`transform react to jsx fragments 1`] = `

View File

@@ -119,8 +119,8 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
}
}
function convertAttribute(node, duplicateChildren) {
let value = convertAttributeValue(node.value || t.booleanLiteral(true));
function convertAttribute(node) {
const value = convertAttributeValue(node.value || t.booleanLiteral(true));
if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
value.value = value.value.replace(/\n\s+/g, ' ');
@@ -130,9 +130,6 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
delete value.extra.raw;
}
}
if (duplicateChildren && duplicateChildren.length > 0) {
value = t.sequenceExpression([...duplicateChildren, value]);
}
if (t.isJSXNamespacedName(node.name)) {
node.name = t.stringLiteral(
@@ -271,32 +268,12 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
);
}
function isChildrenProp(prop) {
return (
t.isJSXAttribute(prop) &&
t.isJSXIdentifier(prop.name) &&
prop.name.name === 'children'
);
}
// Builds props for React.jsx. This function adds children into the props
// and ensures that props is always an object
function buildJSXOpeningElementAttributes(attribs, file, children) {
let _props = [];
const objs = [];
// In order to avoid having duplicate "children" keys, we avoid
// pushing the "children" prop if we have actual children. However,
// the children prop may have side effects, so to be certain
// these side effects are evaluated, we add them to the following prop
// as a sequence expression to preserve order. So:
// <div children={x++} foo={y}>{child}</div> becomes
// React.jsx('div', {foo: (x++, y), children: child});
// duplicateChildren contains the extra children prop values
let duplicateChildren = [];
const hasChildren = children && children.length > 0;
const useBuiltIns = file.opts.useBuiltIns || false;
if (typeof useBuiltIns !== 'boolean') {
throw new Error(
@@ -307,48 +284,24 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
while (attribs.length) {
const prop = attribs.shift();
if (hasChildren && isChildrenProp(prop)) {
duplicateChildren.push(convertAttributeValue(prop.value));
} else if (t.isJSXSpreadAttribute(prop)) {
if (t.isJSXSpreadAttribute(prop)) {
_props = pushProps(_props, objs);
if (duplicateChildren.length > 0) {
objs.push(
t.sequenceExpression([...duplicateChildren, prop.argument]),
);
duplicateChildren = [];
} else {
objs.push(prop.argument);
}
objs.push(prop.argument);
} else {
_props.push(convertAttribute(prop, duplicateChildren));
if (duplicateChildren.length > 0) {
duplicateChildren = [];
}
_props.push(convertAttribute(prop));
}
}
// In React.JSX, children is no longer a separate argument, but passed in
// through the argument object
if (hasChildren) {
if (children && children.length > 0) {
if (children.length === 1) {
_props.push(
t.objectProperty(
t.identifier('children'),
duplicateChildren.length > 0
? t.sequenceExpression([...duplicateChildren, children[0]])
: children[0],
),
);
_props.push(t.objectProperty(t.identifier('children'), children[0]));
} else {
_props.push(
t.objectProperty(
t.identifier('children'),
duplicateChildren.length > 0
? t.sequenceExpression([
...duplicateChildren,
t.arrayExpression(children),
])
: t.arrayExpression(children),
t.arrayExpression(children),
),
);
}

View File

@@ -1,7 +1,7 @@
{
"name": "create-subscription",
"description": "utility for subscribing to external data sources inside React components",
"version": "16.12.0",
"version": "16.9.0",
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git",

View File

@@ -118,10 +118,7 @@ describe('createSubscription', () => {
const Subscription = createSubscription({
getCurrentValue: source => undefined,
subscribe: (source, callback) => {
source.then(
value => callback(value),
value => callback(value),
);
source.then(value => callback(value), value => callback(value));
// (Can't unsubscribe from a Promise)
return () => {};
},
@@ -199,10 +196,7 @@ describe('createSubscription', () => {
const Subscription = createSubscription({
getCurrentValue: source => undefined,
subscribe: (source, callback) => {
source.then(
value => callback(value),
value => callback(value),
);
source.then(value => callback(value), value => callback(value));
// (Can't unsubscribe from a Promise)
return () => {};
},
@@ -458,7 +452,7 @@ describe('createSubscription', () => {
},
() => null,
);
}).toErrorDev('Subscription must specify a getCurrentValue function', {
}).toWarnDev('Subscription must specify a getCurrentValue function', {
withoutStack: true,
});
});
@@ -471,7 +465,7 @@ describe('createSubscription', () => {
},
() => null,
);
}).toErrorDev('Subscription must specify a subscribe function', {
}).toWarnDev('Subscription must specify a subscribe function', {
withoutStack: true,
});
});

View File

@@ -9,6 +9,7 @@
import React from 'react';
import invariant from 'shared/invariant';
import warningWithoutStack from 'shared/warningWithoutStack';
type Unsubscribe = () => void;
@@ -29,29 +30,29 @@ export function createSubscription<Property, Value>(
callback: (value: Value | void) => void,
) => Unsubscribe,
|}>,
): React$ComponentType<{|
): React$ComponentType<{
children: (value: Value | void) => React$Node,
source: Property,
|}> {
}> {
const {getCurrentValue, subscribe} = config;
if (__DEV__) {
if (typeof getCurrentValue !== 'function') {
console.error('Subscription must specify a getCurrentValue function');
}
if (typeof subscribe !== 'function') {
console.error('Subscription must specify a subscribe function');
}
}
warningWithoutStack(
typeof getCurrentValue === 'function',
'Subscription must specify a getCurrentValue function',
);
warningWithoutStack(
typeof subscribe === 'function',
'Subscription must specify a subscribe function',
);
type Props = {|
type Props = {
children: (value: Value) => React$Element<any>,
source: Property,
|};
type State = {|
};
type State = {
source: Property,
value: Value | void,
|};
};
// Reference: https://gist.github.com/bvaughn/d569177d70b50b58bff69c3c4a5353f3
class Subscription extends React.Component<Props, State> {

View File

@@ -1,98 +0,0 @@
# `dom-event-testing-library`
A library for unit testing events via high-level interactions, e.g., `pointerdown`,
that produce realistic and complete DOM event sequences.
There are number of challenges involved in unit testing modules that work with
DOM events.
1. Gesture recognizers may need to support environments with and without support for
the `PointerEvent` API.
2. Gesture recognizers may need to support various user interaction modes including
mouse, touch, and pen use.
3. Gesture recognizers must account for the actual event sequences browsers produce
(e.g., emulated touch and mouse events.)
4. Gesture recognizers must work with "virtual" events produced by tools like
screen-readers.
Writing unit tests to cover all these scenarios is tedious and error prone. This
event testing library is designed to solve these issues by allowing developers to
more easily dispatch events in unit tests, and to more reliably test pointer
interactions using a high-level API based on `PointerEvent`. Here's a basic example:
```js
import {
describeWithPointerEvent,
testWithPointerType,
createEventTarget,
setPointerEvent,
resetActivePointers
} from 'dom-event-testing-library';
describeWithPointerEvent('useTap', hasPointerEvent => {
beforeEach(() => {
// basic PointerEvent mock
setPointerEvent(hasPointerEvent);
});
afterEach(() => {
// clear active pointers between test runs
resetActivePointers();
});
// test all the pointer types supported by the environment
testWithPointerType('pointer down', pointerType => {
const ref = createRef(null);
const onTapStart = jest.fn();
render(() => {
useTap(ref, { onTapStart });
return <div ref={ref} />
});
// create an event target
const target = createEventTarget(ref.current);
// dispatch high-level pointer event
target.pointerdown({ pointerType });
expect(onTapStart).toBeCalled();
});
});
```
This tests the interaction in multiple scenarios. In each case, a realistic DOM
event sequencewith complete mock eventsis produced. When running in a mock
environment without the `PointerEvent` API, the test runs for both `mouse` and
`touch` pointer types. When `touch` is the pointer type it produces emulated mouse
events. When running in a mock environment with the `PointerEvent` API, the test
runs for `mouse`, `touch`, and `pen` pointer types.
It's important to cover all these scenarios because it's very easy to introduce
bugs e.g., double calling of callbacks if not accounting for emulated mouse
events, differences in target capturing between `touch` and `mouse` pointers, and
the different semantics of `button` across event APIs.
Default values are provided for the expected native events properties. They can
also be customized as needed in a test.
```js
target.pointerdown({
button: 0,
buttons: 1,
pageX: 10,
pageY: 10,
pointerType,
// NOTE: use x,y instead of clientX,clientY
x: 10,
y: 10
});
```
Tests that dispatch multiple pointer events will dispatch multi-touch native events
on the target.
```js
// first pointer is active
target.pointerdown({pointerId: 1, pointerType});
// second pointer is active
target.pointerdown({pointerId: 2, pointerType});
```

View File

@@ -1,15 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`describeWithPointerEvent: MouseEvent/TouchEvent provides boolean to tests 1`] = `false`;
exports[`describeWithPointerEvent: MouseEvent/TouchEvent testWithPointerType: mouse 1`] = `"mouse"`;
exports[`describeWithPointerEvent: MouseEvent/TouchEvent testWithPointerType: touch 1`] = `"touch"`;
exports[`describeWithPointerEvent: PointerEvent provides boolean to tests 1`] = `true`;
exports[`describeWithPointerEvent: PointerEvent testWithPointerType: mouse 1`] = `"mouse"`;
exports[`describeWithPointerEvent: PointerEvent testWithPointerType: pen 1`] = `"pen"`;
exports[`describeWithPointerEvent: PointerEvent testWithPointerType: touch 1`] = `"touch"`;

View File

@@ -1,361 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
import {defaultBrowserChromeSize} from '../constants';
import {
createEventTarget,
describeWithPointerEvent,
testWithPointerType,
resetActivePointers,
} from '../index';
/**
* Unit test helpers
*/
describeWithPointerEvent('describeWithPointerEvent', pointerEvent => {
test('provides boolean to tests', () => {
expect(pointerEvent).toMatchSnapshot();
});
testWithPointerType('testWithPointerType', pointerType => {
expect(pointerType).toMatchSnapshot();
});
});
/**
* createEventTarget
*/
describe('createEventTarget', () => {
let node;
beforeEach(() => {
node = document.createElement('div');
});
afterEach(() => {
node = null;
resetActivePointers();
});
test('returns expected API', () => {
const target = createEventTarget(node);
expect(target.node).toEqual(node);
expect(Object.keys(target)).toMatchInlineSnapshot(`
Array [
"node",
"blur",
"click",
"focus",
"keydown",
"keyup",
"scroll",
"virtualclick",
"contextmenu",
"pointercancel",
"pointerdown",
"pointerhover",
"pointermove",
"pointerenter",
"pointerexit",
"pointerup",
"tap",
"setBoundingClientRect",
]
`);
});
/**
* Simple events
*/
describe('.blur()', () => {
test('default', () => {
const target = createEventTarget(node);
node.addEventListener('blur', e => {
expect(e.relatedTarget).toMatchInlineSnapshot(`null`);
});
target.blur();
});
test('custom payload', () => {
const target = createEventTarget(node);
node.addEventListener('blur', e => {
expect(e.relatedTarget).toMatchInlineSnapshot(`null`);
});
target.blur();
});
});
describe('.click()', () => {
test('default', () => {
const target = createEventTarget(node);
node.addEventListener('click', e => {
expect(e.altKey).toEqual(false);
expect(e.button).toEqual(0);
expect(e.buttons).toEqual(0);
expect(e.clientX).toEqual(0);
expect(e.clientY).toEqual(0);
expect(e.ctrlKey).toEqual(false);
expect(e.detail).toEqual(1);
expect(typeof e.getModifierState).toEqual('function');
expect(e.metaKey).toEqual(false);
expect(e.movementX).toEqual(0);
expect(e.movementY).toEqual(0);
expect(e.offsetX).toEqual(0);
expect(e.offsetY).toEqual(0);
expect(e.pageX).toEqual(0);
expect(e.pageY).toEqual(0);
expect(typeof e.preventDefault).toEqual('function');
expect(e.screenX).toEqual(0);
expect(e.screenY).toEqual(defaultBrowserChromeSize);
expect(e.shiftKey).toEqual(false);
expect(typeof e.timeStamp).toEqual('number');
});
target.click();
});
test('custom payload', () => {
const target = createEventTarget(node);
node.addEventListener('click', e => {
expect(e.altKey).toEqual(true);
expect(e.button).toEqual(1);
expect(e.buttons).toEqual(4);
expect(e.clientX).toEqual(10);
expect(e.clientY).toEqual(20);
expect(e.ctrlKey).toEqual(true);
expect(e.metaKey).toEqual(true);
expect(e.movementX).toEqual(1);
expect(e.movementY).toEqual(2);
expect(e.offsetX).toEqual(5);
expect(e.offsetY).toEqual(5);
expect(e.pageX).toEqual(50);
expect(e.pageY).toEqual(50);
expect(e.screenX).toEqual(10);
expect(e.screenY).toEqual(20 + defaultBrowserChromeSize);
expect(e.shiftKey).toEqual(true);
});
target.click({
altKey: true,
button: 1,
buttons: 4,
x: 10,
y: 20,
ctrlKey: true,
metaKey: true,
movementX: 1,
movementY: 2,
offsetX: 5,
offsetY: 5,
pageX: 50,
pageY: 50,
shiftKey: true,
});
});
});
describe('.focus()', () => {
test('default', () => {
const target = createEventTarget(node);
node.addEventListener('focus', e => {
expect(e.relatedTarget).toMatchInlineSnapshot(`null`);
});
target.blur();
});
test('custom payload', () => {
const target = createEventTarget(node);
node.addEventListener('focus', e => {
expect(e.relatedTarget).toMatchInlineSnapshot(`null`);
});
target.blur();
});
});
describe('.keydown()', () => {
test('default', () => {
const target = createEventTarget(node);
node.addEventListener('keydown', e => {
expect(e.altKey).toEqual(false);
expect(e.ctrlKey).toEqual(false);
expect(typeof e.getModifierState).toEqual('function');
expect(e.key).toEqual('');
expect(e.metaKey).toEqual(false);
expect(typeof e.preventDefault).toEqual('function');
expect(e.shiftKey).toEqual(false);
expect(typeof e.timeStamp).toEqual('number');
});
target.keydown();
});
test('custom payload', () => {
const target = createEventTarget(node);
node.addEventListener('keydown', e => {
expect(e.altKey).toEqual(true);
expect(e.ctrlKey).toEqual(true);
expect(e.isComposing).toEqual(true);
expect(e.key).toEqual('Enter');
expect(e.metaKey).toEqual(true);
expect(e.shiftKey).toEqual(true);
});
target.keydown({
altKey: true,
ctrlKey: true,
isComposing: true,
key: 'Enter',
metaKey: true,
shiftKey: true,
});
});
});
describe('.keyup()', () => {
test('default', () => {
const target = createEventTarget(node);
node.addEventListener('keyup', e => {
expect(e.altKey).toEqual(false);
expect(e.ctrlKey).toEqual(false);
expect(typeof e.getModifierState).toEqual('function');
expect(e.key).toEqual('');
expect(e.metaKey).toEqual(false);
expect(typeof e.preventDefault).toEqual('function');
expect(e.shiftKey).toEqual(false);
expect(typeof e.timeStamp).toEqual('number');
});
target.keydown();
});
test('custom payload', () => {
const target = createEventTarget(node);
node.addEventListener('keyup', e => {
expect(e.altKey).toEqual(true);
expect(e.ctrlKey).toEqual(true);
expect(e.isComposing).toEqual(true);
expect(e.key).toEqual('Enter');
expect(e.metaKey).toEqual(true);
expect(e.shiftKey).toEqual(true);
});
target.keyup({
altKey: true,
ctrlKey: true,
isComposing: true,
key: 'Enter',
metaKey: true,
shiftKey: true,
});
});
});
describe('.scroll()', () => {
test('default', () => {
const target = createEventTarget(node);
node.addEventListener('scroll', e => {
expect(e.type).toEqual('scroll');
});
target.scroll();
});
});
describe('.virtualclick()', () => {
test('default', () => {
const target = createEventTarget(node);
node.addEventListener('click', e => {
expect(e.altKey).toEqual(false);
expect(e.button).toEqual(0);
expect(e.buttons).toEqual(0);
expect(e.clientX).toEqual(0);
expect(e.clientY).toEqual(0);
expect(e.ctrlKey).toEqual(false);
expect(e.detail).toEqual(0);
expect(typeof e.getModifierState).toEqual('function');
expect(e.metaKey).toEqual(false);
expect(e.movementX).toEqual(0);
expect(e.movementY).toEqual(0);
expect(e.offsetX).toEqual(0);
expect(e.offsetY).toEqual(0);
expect(e.pageX).toEqual(0);
expect(e.pageY).toEqual(0);
expect(typeof e.preventDefault).toEqual('function');
expect(e.screenX).toEqual(0);
expect(e.screenY).toEqual(0);
expect(e.shiftKey).toEqual(false);
expect(typeof e.timeStamp).toEqual('number');
});
target.virtualclick();
});
test('custom payload', () => {
const target = createEventTarget(node);
node.addEventListener('click', e => {
// expect most of the custom payload to be ignored
expect(e.altKey).toEqual(true);
expect(e.button).toEqual(1);
expect(e.buttons).toEqual(0);
expect(e.clientX).toEqual(0);
expect(e.clientY).toEqual(0);
expect(e.ctrlKey).toEqual(true);
expect(e.detail).toEqual(0);
expect(e.metaKey).toEqual(true);
expect(e.pageX).toEqual(0);
expect(e.pageY).toEqual(0);
expect(e.screenX).toEqual(0);
expect(e.screenY).toEqual(0);
expect(e.shiftKey).toEqual(true);
});
target.virtualclick({
altKey: true,
button: 1,
buttons: 4,
x: 10,
y: 20,
ctrlKey: true,
metaKey: true,
pageX: 50,
pageY: 50,
shiftKey: true,
});
});
});
/**
* Complex event sequences
*
* ...coming soon
*/
/**
* Other APIs
*/
test('.setBoundingClientRect()', () => {
const target = createEventTarget(node);
expect(node.getBoundingClientRect()).toMatchInlineSnapshot(`
Object {
"bottom": 0,
"height": 0,
"left": 0,
"right": 0,
"top": 0,
"width": 0,
}
`);
target.setBoundingClientRect({x: 10, y: 20, width: 100, height: 200});
expect(node.getBoundingClientRect()).toMatchInlineSnapshot(`
Object {
"bottom": 220,
"height": 200,
"left": 10,
"right": 110,
"top": 20,
"width": 100,
}
`);
});
});

View File

@@ -1,65 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
export const defaultPointerId = 1;
export const defaultPointerSize = 23;
export const defaultBrowserChromeSize = 50;
/**
* Button property
* This property only guarantees to indicate which buttons are pressed during events caused by pressing or
* releasing one or multiple buttons. As such, it is not reliable for events such as 'mouseenter', 'mouseleave',
* 'mouseover', 'mouseout' or 'mousemove'. Furthermore, the semantics differ for PointerEvent, where the value
* for 'pointermove' will always be -1.
*/
export const buttonType = {
// no change since last event
none: -1,
// left-mouse
// touch contact
// pen contact
primary: 0,
// right-mouse
// pen barrel button
secondary: 2,
// middle mouse
auxiliary: 1,
// back mouse
back: 3,
// forward mouse
forward: 4,
// pen eraser
eraser: 5,
};
/**
* Buttons bitmask
*/
export const buttonsType = {
none: 0,
// left-mouse
// touch contact
// pen contact
primary: 1,
// right-mouse
// pen barrel button
secondary: 2,
// middle mouse
auxiliary: 4,
// back mouse
back: 8,
// forward mouse
forward: 16,
// pen eraser
eraser: 32,
};

View File

@@ -1,361 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
import {
buttonType,
buttonsType,
defaultPointerId,
defaultPointerSize,
defaultBrowserChromeSize,
} from './constants';
import * as domEvents from './domEvents';
import {hasPointerEvent, platform} from './domEnvironment';
import * as touchStore from './touchStore';
/**
* Converts a PointerEvent payload to a Touch
*/
function createTouch(target, payload) {
const {
height = defaultPointerSize,
pageX,
pageY,
pointerId,
pressure = 1,
twist = 0,
width = defaultPointerSize,
x = 0,
y = 0,
} = payload;
return {
clientX: x,
clientY: y,
force: pressure,
identifier: pointerId,
pageX: pageX || x,
pageY: pageY || y,
radiusX: width / 2,
radiusY: height / 2,
rotationAngle: twist,
target,
screenX: x,
screenY: y + defaultBrowserChromeSize,
};
}
/**
* Converts a PointerEvent to a TouchEvent
*/
function createTouchEventPayload(target, touch, payload) {
const {
altKey = false,
ctrlKey = false,
metaKey = false,
preventDefault,
shiftKey = false,
timeStamp,
} = payload;
return {
altKey,
changedTouches: [touch],
ctrlKey,
metaKey,
preventDefault,
shiftKey,
targetTouches: touchStore.getTargetTouches(target),
timeStamp,
touches: touchStore.getTouches(),
};
}
function getPointerType(payload) {
let pointerType = 'mouse';
if (payload != null && payload.pointerType != null) {
pointerType = payload.pointerType;
}
return pointerType;
}
/**
* Pointer events sequences.
*
* Creates representative browser event sequences for high-level gestures based on pointers.
* This allows unit tests to be written in terms of simple pointer interactions while testing
* that the responses to those interactions account for the complex sequence of events that
* browsers produce as a result.
*
* Every time a new pointer touches the surface a 'touchstart' event should be dispatched.
* - 'changedTouches' contains the new touch.
* - 'targetTouches' contains all the active pointers for the target.
* - 'touches' contains all the active pointers on the surface.
*
* Every time an existing pointer moves a 'touchmove' event should be dispatched.
* - 'changedTouches' contains the updated touch.
*
* Every time an existing pointer leaves the surface a 'touchend' event should be dispatched.
* - 'changedTouches' contains the released touch.
* - 'targetTouches' contains any of the remaining active pointers for the target.
*/
export function contextmenu(
target,
defaultPayload,
{pointerType = 'mouse', modified} = {},
) {
const dispatch = arg => target.dispatchEvent(arg);
const payload = {
pointerId: defaultPointerId,
pointerType,
...defaultPayload,
};
const preventDefault = payload.preventDefault;
if (pointerType === 'touch') {
if (hasPointerEvent()) {
dispatch(
domEvents.pointerdown({
...payload,
button: buttonType.primary,
buttons: buttonsType.primary,
}),
);
}
const touch = createTouch(target, payload);
touchStore.addTouch(touch);
const touchEventPayload = createTouchEventPayload(target, touch, payload);
dispatch(domEvents.touchstart(touchEventPayload));
dispatch(
domEvents.contextmenu({
button: buttonType.primary,
buttons: buttonsType.none,
preventDefault,
}),
);
touchStore.removeTouch(touch);
} else if (pointerType === 'mouse') {
if (modified === true) {
const button = buttonType.primary;
const buttons = buttonsType.primary;
const ctrlKey = true;
if (hasPointerEvent()) {
dispatch(
domEvents.pointerdown({button, buttons, ctrlKey, pointerType}),
);
}
dispatch(domEvents.mousedown({button, buttons, ctrlKey}));
if (platform.get() === 'mac') {
dispatch(
domEvents.contextmenu({button, buttons, ctrlKey, preventDefault}),
);
}
} else {
const button = buttonType.secondary;
const buttons = buttonsType.secondary;
if (hasPointerEvent()) {
dispatch(domEvents.pointerdown({button, buttons, pointerType}));
}
dispatch(domEvents.mousedown({button, buttons}));
dispatch(domEvents.contextmenu({button, buttons, preventDefault}));
}
}
}
export function pointercancel(target, defaultPayload) {
const dispatchEvent = arg => target.dispatchEvent(arg);
const pointerType = getPointerType(defaultPayload);
const payload = {
pointerId: defaultPointerId,
pointerType,
...defaultPayload,
};
if (hasPointerEvent()) {
dispatchEvent(domEvents.pointercancel(payload));
} else {
if (pointerType === 'mouse') {
dispatchEvent(domEvents.dragstart(payload));
} else {
const touch = createTouch(target, payload);
touchStore.removeTouch(touch);
const touchEventPayload = createTouchEventPayload(target, touch, payload);
dispatchEvent(domEvents.touchcancel(touchEventPayload));
}
}
}
export function pointerdown(target, defaultPayload) {
const dispatch = arg => target.dispatchEvent(arg);
const pointerType = getPointerType(defaultPayload);
const payload = {
button: buttonType.primary,
buttons: buttonsType.primary,
pointerId: defaultPointerId,
pointerType,
...defaultPayload,
};
if (pointerType === 'mouse') {
if (hasPointerEvent()) {
dispatch(domEvents.pointerover(payload));
dispatch(domEvents.pointerenter(payload));
}
dispatch(domEvents.mouseover(payload));
dispatch(domEvents.mouseenter(payload));
if (hasPointerEvent()) {
dispatch(domEvents.pointerdown(payload));
}
dispatch(domEvents.mousedown(payload));
if (document.activeElement !== target) {
dispatch(domEvents.focus());
}
} else {
if (hasPointerEvent()) {
dispatch(domEvents.pointerover(payload));
dispatch(domEvents.pointerenter(payload));
dispatch(domEvents.pointerdown(payload));
}
const touch = createTouch(target, payload);
touchStore.addTouch(touch);
const touchEventPayload = createTouchEventPayload(target, touch, payload);
dispatch(domEvents.touchstart(touchEventPayload));
if (hasPointerEvent()) {
dispatch(domEvents.gotpointercapture(payload));
}
}
}
export function pointerenter(target, defaultPayload) {
const dispatch = arg => target.dispatchEvent(arg);
const payload = {
pointerId: defaultPointerId,
...defaultPayload,
};
if (hasPointerEvent()) {
dispatch(domEvents.pointerover(payload));
dispatch(domEvents.pointerenter(payload));
}
dispatch(domEvents.mouseover(payload));
dispatch(domEvents.mouseenter(payload));
}
export function pointerexit(target, defaultPayload) {
const dispatch = arg => target.dispatchEvent(arg);
const payload = {
pointerId: defaultPointerId,
...defaultPayload,
};
if (hasPointerEvent()) {
dispatch(domEvents.pointerout(payload));
dispatch(domEvents.pointerleave(payload));
}
dispatch(domEvents.mouseout(payload));
dispatch(domEvents.mouseleave(payload));
}
export function pointerhover(target, defaultPayload) {
const dispatch = arg => target.dispatchEvent(arg);
const payload = {
pointerId: defaultPointerId,
...defaultPayload,
};
if (hasPointerEvent()) {
dispatch(domEvents.pointermove(payload));
}
dispatch(domEvents.mousemove(payload));
}
export function pointermove(target, defaultPayload) {
const dispatch = arg => target.dispatchEvent(arg);
const pointerType = getPointerType(defaultPayload);
const payload = {
pointerId: defaultPointerId,
pointerType,
...defaultPayload,
};
if (hasPointerEvent()) {
dispatch(
domEvents.pointermove({
pressure: pointerType === 'touch' ? 1 : 0.5,
...payload,
}),
);
} else {
if (pointerType === 'mouse') {
dispatch(domEvents.mousemove(payload));
} else {
const touch = createTouch(target, payload);
touchStore.updateTouch(touch);
const touchEventPayload = createTouchEventPayload(target, touch, payload);
dispatch(domEvents.touchmove(touchEventPayload));
}
}
}
export function pointerup(target, defaultPayload) {
const dispatch = arg => target.dispatchEvent(arg);
const pointerType = getPointerType(defaultPayload);
const payload = {
pointerId: defaultPointerId,
pointerType,
...defaultPayload,
};
if (pointerType === 'mouse') {
if (hasPointerEvent()) {
dispatch(domEvents.pointerup(payload));
}
dispatch(domEvents.mouseup(payload));
dispatch(domEvents.click(payload));
} else {
if (hasPointerEvent()) {
dispatch(domEvents.pointerup(payload));
dispatch(domEvents.lostpointercapture(payload));
dispatch(domEvents.pointerout(payload));
dispatch(domEvents.pointerleave(payload));
}
const touch = createTouch(target, payload);
touchStore.removeTouch(touch);
const touchEventPayload = createTouchEventPayload(target, touch, payload);
dispatch(domEvents.touchend(touchEventPayload));
dispatch(domEvents.mouseover(payload));
dispatch(domEvents.mousemove(payload));
dispatch(domEvents.mousedown(payload));
if (document.activeElement !== target) {
dispatch(domEvents.focus());
}
dispatch(domEvents.mouseup(payload));
dispatch(domEvents.click(payload));
}
}
/**
* This function should be called after each test to ensure the touchStore is cleared
* in cases where the mock pointers weren't released before the test completed
* (e.g., a test failed or ran a partial gesture).
*/
export function resetActivePointers() {
touchStore.clear();
}

View File

@@ -1,5 +0,0 @@
{
"private": true,
"name": "dom-event-testing-library",
"version": "0.0.0"
}

View File

@@ -1,35 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
import {hasPointerEvent, setPointerEvent} from './domEnvironment';
export function describeWithPointerEvent(message, describeFn) {
const pointerEvent = 'PointerEvent';
const fallback = 'MouseEvent/TouchEvent';
describe.each`
value | name
${true} | ${pointerEvent}
${false} | ${fallback}
`(`${message}: $name`, entry => {
const hasPointerEvents = entry.value;
setPointerEvent(hasPointerEvents);
describeFn(hasPointerEvents);
});
}
export function testWithPointerType(message, testFn) {
const table = hasPointerEvent()
? ['mouse', 'touch', 'pen']
: ['mouse', 'touch'];
test.each(table)(`${message}: %s`, pointerType => {
testFn(pointerType);
});
}

View File

@@ -1,82 +0,0 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
/**
* Touch events state machine.
*
* Keeps track of the active pointers and allows them to be reflected in touch events.
*/
const activeTouches = new Map();
export function addTouch(touch) {
const identifier = touch.identifier;
const target = touch.target;
if (!activeTouches.has(target)) {
activeTouches.set(target, new Map());
}
if (activeTouches.get(target).get(identifier)) {
// Do not allow existing touches to be overwritten
console.error(
'Touch with identifier %s already exists. Did not record touch start.',
identifier,
);
} else {
activeTouches.get(target).set(identifier, touch);
}
}
export function updateTouch(touch) {
const identifier = touch.identifier;
const target = touch.target;
if (activeTouches.get(target) != null) {
activeTouches.get(target).set(identifier, touch);
} else {
console.error(
'Touch with identifier %s does not exist. Cannot record touch move without a touch start.',
identifier,
);
}
}
export function removeTouch(touch) {
const identifier = touch.identifier;
const target = touch.target;
if (activeTouches.get(target) != null) {
if (activeTouches.get(target).has(identifier)) {
activeTouches.get(target).delete(identifier);
} else {
console.error(
'Touch with identifier %s does not exist. Cannot record touch end without a touch start.',
identifier,
);
}
}
}
export function getTouches() {
const touches = [];
activeTouches.forEach((_, target) => {
touches.push(...getTargetTouches(target));
});
return touches;
}
export function getTargetTouches(target) {
if (activeTouches.get(target) != null) {
return Array.from(activeTouches.get(target).values());
}
return [];
}
export function clear() {
activeTouches.clear();
}

View File

@@ -6,7 +6,7 @@ It is a part of the [Hooks API](https://reactjs.org/docs/hooks-intro.html) for R
## Installation
**Note: If you're using Create React App, please use `react-scripts` >= 3 instead of adding it directly.**
**Note: If you're using Create React App, please wait for a corresponding release of `react-scripts` that includes this rule instead of adding it directly.**
Assuming you already have ESLint installed, run:

View File

@@ -3154,37 +3154,6 @@ const tests = {
`and use that variable in the cleanup function.`,
],
},
{
code: `
function MyComponent() {
const myRef = useRef();
useLayoutEffect_SAFE_FOR_SSR(() => {
const handleMove = () => {};
myRef.current.addEventListener('mousemove', handleMove);
return () => myRef.current.removeEventListener('mousemove', handleMove);
});
return <div ref={myRef} />;
}
`,
output: `
function MyComponent() {
const myRef = useRef();
useLayoutEffect_SAFE_FOR_SSR(() => {
const handleMove = () => {};
myRef.current.addEventListener('mousemove', handleMove);
return () => myRef.current.removeEventListener('mousemove', handleMove);
});
return <div ref={myRef} />;
}
`,
errors: [
`The ref value 'myRef.current' will likely have changed by the time ` +
`this effect cleanup function runs. If this ref points to a node ` +
`rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
`and use that variable in the cleanup function.`,
],
options: [{additionalHooks: 'useLayoutEffect_SAFE_FOR_SSR'}],
},
{
// Autofix ignores constant primitives (leaving the ones that are there).
code: `

View File

@@ -141,46 +141,6 @@ const tests = {
return useHook1(useHook2());
}
`,
`
// Valid because hooks can be used in anonymous arrow-function arguments
// to forwardRef.
const FancyButton = React.forwardRef((props, ref) => {
useHook();
return <button {...props} ref={ref} />
});
`,
`
// Valid because hooks can be used in anonymous function arguments to
// forwardRef.
const FancyButton = React.forwardRef(function (props, ref) {
useHook();
return <button {...props} ref={ref} />
});
`,
`
// Valid because hooks can be used in anonymous function arguments to
// forwardRef.
const FancyButton = forwardRef(function (props, ref) {
useHook();
return <button {...props} ref={ref} />
});
`,
`
// Valid because hooks can be used in anonymous function arguments to
// React.memo.
const MemoizedFunction = React.memo(props => {
useHook();
return <button {...props} />
});
`,
`
// Valid because hooks can be used in anonymous function arguments to
// memo.
const MemoizedFunction = memo(function (props) {
useHook();
return <button {...props} />
});
`,
`
// Valid because classes can call functions.
// We don't consider these to be hooks.
@@ -302,24 +262,6 @@ const tests = {
});
}
`,
`
// This is valid because "use"-prefixed functions called in
// unnamed function arguments are not assumed to be hooks.
React.unknownFunction((foo, bar) => {
if (foo) {
useNotAHook(bar)
}
});
`,
`
// This is valid because "use"-prefixed functions called in
// unnamed function arguments are not assumed to be hooks.
unknownFunction(function(foo, bar) {
if (foo) {
useNotAHook(bar)
}
});
`,
`
// Regression test for incorrectly flagged valid code.
function RegressionTest() {
@@ -495,32 +437,6 @@ const tests = {
`,
errors: [genericError('useHookInsideCallback')],
},
{
code: `
// Invalid because it's a common misunderstanding.
// We *could* make it valid but the runtime error could be confusing.
const ComponentWithHookInsideCallback = React.forwardRef((props, ref) => {
useEffect(() => {
useHookInsideCallback();
});
return <button {...props} ref={ref} />
});
`,
errors: [genericError('useHookInsideCallback')],
},
{
code: `
// Invalid because it's a common misunderstanding.
// We *could* make it valid but the runtime error could be confusing.
const ComponentWithHookInsideCallback = React.memo(props => {
useEffect(() => {
useHookInsideCallback();
});
return <button {...props} />
});
`,
errors: [genericError('useHookInsideCallback')],
},
{
code: `
// Invalid because it's a common misunderstanding.
@@ -779,55 +695,6 @@ const tests = {
// conditionalError('useState'),
],
},
{
code: `
// Invalid because it's dangerous and might not warn otherwise.
// This *must* be invalid.
const FancyButton = React.forwardRef((props, ref) => {
if (props.fancy) {
useCustomHook();
}
return <button ref={ref}>{props.children}</button>;
});
`,
errors: [conditionalError('useCustomHook')],
},
{
code: `
// Invalid because it's dangerous and might not warn otherwise.
// This *must* be invalid.
const FancyButton = forwardRef(function(props, ref) {
if (props.fancy) {
useCustomHook();
}
return <button ref={ref}>{props.children}</button>;
});
`,
errors: [conditionalError('useCustomHook')],
},
{
code: `
// Invalid because it's dangerous and might not warn otherwise.
// This *must* be invalid.
const MemoizedButton = memo(function(props) {
if (props.fancy) {
useCustomHook();
}
return <button>{props.children}</button>;
});
`,
errors: [conditionalError('useCustomHook')],
},
{
code: `
// This is invalid because "use"-prefixed functions used in named
// functions are assumed to be hooks.
React.unknownFunction(function notAComponent(foo, bar) {
useProbablyAHook(bar)
});
`,
errors: [functionError('useProbablyAHook', 'notAComponent')],
},
{
code: `
// Invalid because it's dangerous.
@@ -891,8 +758,8 @@ function loopError(hook) {
function functionError(hook, fn) {
return {
message:
`React Hook "${hook}" is called in function "${fn}" that is neither ` +
'a React function component nor a custom React Hook function.',
`React Hook "${hook}" is called in function "${fn}" which is neither ` +
'a React function component or a custom React Hook function.',
};
}

View File

@@ -1,7 +1,7 @@
{
"name": "eslint-plugin-react-hooks",
"description": "ESLint rules for React Hooks",
"version": "2.3.0",
"version": "2.0.1",
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git",

View File

@@ -81,7 +81,7 @@ export default {
// Get the reactive hook node.
const reactiveHook = node.parent.callee;
const reactiveHookName = getNodeWithoutReactNamespace(reactiveHook).name;
const isEffect = /Effect($|[^a-z])/g.test(reactiveHookName);
const isEffect = reactiveHookName.endsWith('Effect');
// Get the declared dependencies for this reactive hook. If there is no
// second argument then the reactive callback will re-run on every render.
@@ -613,15 +613,21 @@ export default {
context.report({
node: declaredDependencyNode,
message:
`The ${declaredDependencyNode.raw} literal is not a valid dependency ` +
`The ${
declaredDependencyNode.raw
} literal is not a valid dependency ` +
`because it never changes. ` +
`Did you mean to include ${declaredDependencyNode.value} in the array instead?`,
`Did you mean to include ${
declaredDependencyNode.value
} in the array instead?`,
});
} else {
context.report({
node: declaredDependencyNode,
message:
`The ${declaredDependencyNode.raw} literal is not a valid dependency ` +
`The ${
declaredDependencyNode.raw
} literal is not a valid dependency ` +
'because it never changes. You can safely remove it.',
});
}
@@ -691,16 +697,22 @@ export default {
bareFunctions.forEach(({fn, suggestUseCallback}) => {
let message =
`The '${fn.name.name}' function makes the dependencies of ` +
`${reactiveHookName} Hook (at line ${declaredDependenciesNode.loc.start.line}) ` +
`${reactiveHookName} Hook (at line ${
declaredDependenciesNode.loc.start.line
}) ` +
`change on every render.`;
if (suggestUseCallback) {
message +=
` To fix this, ` +
`wrap the '${fn.name.name}' definition into its own useCallback() Hook.`;
`wrap the '${
fn.name.name
}' definition into its own useCallback() Hook.`;
} else {
message +=
` Move it inside the ${reactiveHookName} callback. ` +
`Alternatively, wrap the '${fn.name.name}' definition into its own useCallback() Hook.`;
`Alternatively, wrap the '${
fn.name.name
}' definition into its own useCallback() Hook.`;
}
// TODO: What if the function needs to change on every render anyway?
// Should we suggest removing effect deps as an appropriate fix too?

View File

@@ -54,41 +54,6 @@ function isComponentName(node) {
}
}
function isReactFunction(node, functionName) {
return (
node.name === functionName ||
(node.type === 'MemberExpression' &&
node.object.name === 'React' &&
node.property.name === functionName)
);
}
/**
* Checks if the node is a callback argument of forwardRef. This render function
* should follow the rules of hooks.
*/
function isForwardRefCallback(node) {
return !!(
node.parent &&
node.parent.callee &&
isReactFunction(node.parent.callee, 'forwardRef')
);
}
/**
* Checks if the node is a callback argument of React.memo. This anonymous
* functional component should follow the rules of hooks.
*/
function isMemoCallback(node) {
return !!(
node.parent &&
node.parent.callee &&
isReactFunction(node.parent.callee, 'memo')
);
}
function isInsideComponentOrHook(node) {
while (node) {
const functionName = getFunctionName(node);
@@ -97,9 +62,6 @@ function isInsideComponentOrHook(node) {
return true;
}
}
if (isForwardRefCallback(node) || isMemoCallback(node)) {
return true;
}
node = node.parent;
}
return false;
@@ -328,8 +290,7 @@ export default {
// `undefined` then we know either that we have an anonymous function
// expression or our code path is not in a function. In both cases we
// will want to error since neither are React function components or
// hook functions - unless it is an anonymous function argument to
// forwardRef or memo.
// hook functions.
const codePathFunctionName = getFunctionName(codePathNode);
// This is a valid code path for React hooks if we are directly in a React
@@ -340,7 +301,7 @@ export default {
const isDirectlyInsideComponentOrHook = codePathFunctionName
? isComponentName(codePathFunctionName) ||
isHook(codePathFunctionName)
: isForwardRefCallback(codePathNode) || isMemoCallback(codePathNode);
: false;
// Compute the earliest finalizer level using information from the
// cache. We expect all reachable final segments to have a cache entry
@@ -467,7 +428,7 @@ export default {
const message =
`React Hook "${context.getSource(hook)}" is called in ` +
`function "${context.getSource(codePathFunctionName)}" ` +
'that is neither a React function component nor a custom ' +
'which is neither a React function component or a custom ' +
'React Hook function.';
context.report({node: hook, message});
} else if (codePathNode.type === 'Program') {

View File

@@ -19,7 +19,7 @@
},
"homepage": "https://reactjs.org/",
"peerDependencies": {
"jest": "^23.0.1 || ^24.0.0",
"jest": "^23.0.1",
"scheduler": "^0.15.0"
},
"files": [

View File

@@ -1,6 +1,6 @@
{
"name": "jest-react",
"version": "0.10.0",
"version": "0.7.0",
"description": "Jest matchers and utilities for testing React components.",
"main": "index.js",
"repository": {
@@ -19,7 +19,7 @@
},
"homepage": "https://reactjs.org/",
"peerDependencies": {
"jest": "^23.0.1 || ^24.0.0",
"jest": "^23.0.1",
"react": "^16.0.0",
"react-test-renderer": "^16.0.0"
},

View File

@@ -132,10 +132,10 @@ export function getListener(inst: Fiber, registrationName: string) {
*/
function extractPluginEvents(
topLevelType: TopLevelType,
eventSystemFlags: EventSystemFlags,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
nativeEventTarget: EventTarget,
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
let events = null;
for (let i = 0; i < plugins.length; i++) {
@@ -144,10 +144,10 @@ function extractPluginEvents(
if (possiblePlugin) {
const extractedEvents = possiblePlugin.extractEvents(
topLevelType,
eventSystemFlags,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
);
if (extractedEvents) {
events = accumulateInto(events, extractedEvents);
@@ -159,17 +159,17 @@ function extractPluginEvents(
export function runExtractedPluginEventsInBatch(
topLevelType: TopLevelType,
eventSystemFlags: EventSystemFlags,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
nativeEventTarget: EventTarget,
) {
const events = extractPluginEvents(
topLevelType,
eventSystemFlags,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
);
runEventsInBatch(events);
}

View File

@@ -16,7 +16,7 @@ import type {
import invariant from 'shared/invariant';
type NamesToPlugins = {[key: PluginName]: PluginModule<AnyNativeEvent>, ...};
type NamesToPlugins = {[key: PluginName]: PluginModule<AnyNativeEvent>};
type EventPluginOrder = null | Array<PluginName>;
/**

View File

@@ -7,6 +7,7 @@
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
import invariant from 'shared/invariant';
import warningWithoutStack from 'shared/warningWithoutStack';
export let getFiberCurrentPropsFromNode = null;
export let getInstanceFromNode = null;
@@ -21,12 +22,11 @@ export function setComponentTree(
getInstanceFromNode = getInstanceFromNodeImpl;
getNodeFromInstance = getNodeFromInstanceImpl;
if (__DEV__) {
if (!getNodeFromInstance || !getInstanceFromNode) {
console.error(
'EventPluginUtils.setComponentTree(...): Injected ' +
'module is missing getNodeFromInstance or getInstanceFromNode.',
);
}
warningWithoutStack(
getNodeFromInstance && getInstanceFromNode,
'EventPluginUtils.setComponentTree(...): Injected ' +
'module is missing getNodeFromInstance or getInstanceFromNode.',
);
}
}
@@ -40,19 +40,20 @@ if (__DEV__) {
const listenersLen = listenersIsArr
? dispatchListeners.length
: dispatchListeners
? 1
: 0;
? 1
: 0;
const instancesIsArr = Array.isArray(dispatchInstances);
const instancesLen = instancesIsArr
? dispatchInstances.length
: dispatchInstances
? 1
: 0;
? 1
: 0;
if (instancesIsArr !== listenersIsArr || instancesLen !== listenersLen) {
console.error('EventPluginUtils: Invalid `event`.');
}
warningWithoutStack(
instancesIsArr === listenersIsArr && instancesLen === listenersLen,
'EventPluginUtils: Invalid `event`.',
);
};
}

View File

@@ -10,6 +10,7 @@ import {
traverseTwoPhase,
traverseEnterLeave,
} from 'shared/ReactTreeTraversal';
import warningWithoutStack from 'shared/warningWithoutStack';
import {getListener} from './EventPluginHub';
import accumulateInto from './accumulateInto';
@@ -45,9 +46,7 @@ function listenerAtPhase(inst, event, propagationPhase: PropagationPhases) {
*/
function accumulateDirectionalDispatches(inst, phase, event) {
if (__DEV__) {
if (!inst) {
console.error('Dispatching inst must not be null');
}
warningWithoutStack(inst, 'Dispatching inst must not be null');
}
const listener = listenerAtPhase(inst, event, phase);
if (listener) {

View File

@@ -15,4 +15,3 @@ export const IS_PASSIVE = 1 << 2;
export const IS_ACTIVE = 1 << 3;
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
export const IS_REPLAYED = 1 << 5;
export const IS_FIRST_ANCESTOR = 1 << 6;

View File

@@ -15,7 +15,7 @@ import type {
import type {TopLevelType} from './TopLevelEventTypes';
import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
export type EventTypes = {[key: string]: DispatchConfig, ...};
export type EventTypes = {[key: string]: DispatchConfig};
export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
@@ -25,11 +25,10 @@ export type PluginModule<NativeEvent> = {
eventTypes: EventTypes,
extractEvents: (
topLevelType: TopLevelType,
eventSystemFlags: EventSystemFlags,
targetInst: null | Fiber,
nativeTarget: NativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
nativeEventTarget: EventTarget,
) => ?ReactSyntheticEvent,
tapMoveThreshold?: number,
...
};

View File

@@ -33,12 +33,8 @@ function restoreStateOfTarget(target) {
'setRestoreImplementation() needs to be called to handle a target for controlled ' +
'events. This error is likely caused by a bug in React. Please file an issue.',
);
const stateNode = internalInstance.stateNode;
// Guard against Fiber being unmounted.
if (stateNode) {
const props = getFiberCurrentPropsFromNode(stateNode);
restoreImpl(internalInstance.stateNode, internalInstance.type, props);
}
const props = getFiberCurrentPropsFromNode(internalInstance.stateNode);
restoreImpl(internalInstance.stateNode, internalInstance.type, props);
}
export function setRestoreImplementation(

View File

@@ -10,7 +10,7 @@ import {
restoreStateIfNeeded,
} from './ReactControlledComponent';
import {enableDeprecatedFlareAPI} from 'shared/ReactFeatureFlags';
import {enableFlareAPI} from 'shared/ReactFeatureFlags';
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
// Used as a way to call batchedUpdates when we don't have a reference to
@@ -118,9 +118,8 @@ export function flushDiscreteUpdatesIfNeeded(timeStamp: number) {
// behaviour as we had before this change, so the risks are low.
if (
!isInsideEventHandler &&
(!enableDeprecatedFlareAPI ||
timeStamp === 0 ||
lastFlushedEventTimeStamp !== timeStamp)
(!enableFlareAPI ||
(timeStamp === 0 || lastFlushedEventTimeStamp !== timeStamp))
) {
lastFlushedEventTimeStamp = timeStamp;
flushDiscreteUpdatesImpl();

View File

@@ -12,17 +12,17 @@ import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {EventPriority} from 'shared/ReactTypes';
import type {TopLevelType} from './TopLevelEventTypes';
export type DispatchConfig = {|
export type DispatchConfig = {
dependencies: Array<TopLevelType>,
phasedRegistrationNames?: {|
phasedRegistrationNames?: {
bubbled: string,
captured: string,
|},
},
registrationName?: string,
eventPriority: EventPriority,
|};
};
export type ReactSyntheticEvent = {|
export type ReactSyntheticEvent = {
dispatchConfig: DispatchConfig,
getPooled: (
dispatchConfig: DispatchConfig,
@@ -31,4 +31,4 @@ export type ReactSyntheticEvent = {|
nativeEventTarget: EventTarget,
) => ReactSyntheticEvent,
isPersistent: () => boolean,
|} & SyntheticEvent<>;
} & SyntheticEvent<>;

View File

@@ -350,10 +350,10 @@ function setResponderAndExtractTransfer(
const shouldSetEventType = isStartish(topLevelType)
? eventTypes.startShouldSetResponder
: isMoveish(topLevelType)
? eventTypes.moveShouldSetResponder
: topLevelType === TOP_SELECTION_CHANGE
? eventTypes.selectionChangeShouldSetResponder
: eventTypes.scrollShouldSetResponder;
? eventTypes.moveShouldSetResponder
: topLevelType === TOP_SELECTION_CHANGE
? eventTypes.selectionChangeShouldSetResponder
: eventTypes.scrollShouldSetResponder;
// TODO: stop one short of the current responder.
const bubbleShouldSetFrom = !responderInst
@@ -504,10 +504,10 @@ const ResponderEventPlugin = {
*/
extractEvents: function(
topLevelType,
eventSystemFlags,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
) {
if (isStartish(topLevelType)) {
trackedTouchCount += 1;
@@ -515,11 +515,9 @@ const ResponderEventPlugin = {
if (trackedTouchCount >= 0) {
trackedTouchCount -= 1;
} else {
if (__DEV__) {
console.warn(
'Ended a touch event which was not counted in `trackedTouchCount`.',
);
}
console.warn(
'Ended a touch event which was not counted in `trackedTouchCount`.',
);
return null;
}
}
@@ -550,10 +548,10 @@ const ResponderEventPlugin = {
const incrementalTouch = isResponderTouchStart
? eventTypes.responderStart
: isResponderTouchMove
? eventTypes.responderMove
: isResponderTouchEnd
? eventTypes.responderEnd
: null;
? eventTypes.responderMove
: isResponderTouchEnd
? eventTypes.responderEnd
: null;
if (incrementalTouch) {
const gesture = ResponderSyntheticEvent.getPooled(
@@ -577,8 +575,8 @@ const ResponderEventPlugin = {
const finalTouch = isResponderTerminate
? eventTypes.responderTerminate
: isResponderRelease
? eventTypes.responderRelease
: null;
? eventTypes.responderRelease
: null;
if (finalTouch) {
const finalEvent = ResponderSyntheticEvent.getPooled(
finalTouch,

View File

@@ -8,6 +8,7 @@
*/
import invariant from 'shared/invariant';
import warningWithoutStack from 'shared/warningWithoutStack';
import {isStartish, isMoveish, isEndish} from './ResponderTopLevelEventTypes';
@@ -16,7 +17,7 @@ import {isStartish, isMoveish, isEndish} from './ResponderTopLevelEventTypes';
* should typically only see IDs in the range of 1-20 because IDs get recycled
* when touches end and start again.
*/
type TouchRecord = {|
type TouchRecord = {
touchActive: boolean,
startPageX: number,
startPageY: number,
@@ -27,7 +28,7 @@ type TouchRecord = {|
previousPageX: number,
previousPageY: number,
previousTimeStamp: number,
|};
};
const MAX_TOUCH_BANK = 20;
const touchBank: Array<TouchRecord> = [];
@@ -46,12 +47,10 @@ type Touch = {
pageX: number,
pageY: number,
timestamp: number,
...
};
type TouchEvent = {
changedTouches: Array<Touch>,
touches: Array<Touch>,
...
};
function timestampForTouch(touch: Touch): number {
@@ -96,14 +95,13 @@ function resetTouchRecord(touchRecord: TouchRecord, touch: Touch): void {
function getTouchIdentifier({identifier}: Touch): number {
invariant(identifier != null, 'Touch object is missing identifier.');
if (__DEV__) {
if (identifier > MAX_TOUCH_BANK) {
console.error(
'Touch identifier %s is greater than maximum supported %s which causes ' +
'performance issues backfilling array locations for all of the indices.',
identifier,
MAX_TOUCH_BANK,
);
}
warningWithoutStack(
identifier <= MAX_TOUCH_BANK,
'Touch identifier %s is greater than maximum supported %s which causes ' +
'performance issues backfilling array locations for all of the indices.',
identifier,
MAX_TOUCH_BANK,
);
}
return identifier;
}
@@ -131,15 +129,12 @@ function recordTouchMove(touch: Touch): void {
touchRecord.currentTimeStamp = timestampForTouch(touch);
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
} else {
if (__DEV__) {
console.warn(
'Cannot record touch move without a touch start.\n' +
'Touch Move: %s\n' +
'Touch Bank: %s',
printTouch(touch),
printTouchBank(),
);
}
console.warn(
'Cannot record touch move without a touch start.\n' + 'Touch Move: %s\n',
'Touch Bank: %s',
printTouch(touch),
printTouchBank(),
);
}
}
@@ -155,15 +150,12 @@ function recordTouchEnd(touch: Touch): void {
touchRecord.currentTimeStamp = timestampForTouch(touch);
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
} else {
if (__DEV__) {
console.warn(
'Cannot record touch end without a touch start.\n' +
'Touch End: %s\n' +
'Touch Bank: %s',
printTouch(touch),
printTouchBank(),
);
}
console.warn(
'Cannot record touch end without a touch start.\n' + 'Touch End: %s\n',
'Touch Bank: %s',
printTouch(touch),
printTouchBank(),
);
}
}
@@ -208,9 +200,10 @@ const ResponderTouchHistoryStore = {
}
if (__DEV__) {
const activeRecord = touchBank[touchHistory.indexOfSingleActiveTouch];
if (activeRecord == null || !activeRecord.touchActive) {
console.error('Cannot find single active touch.');
}
warningWithoutStack(
activeRecord != null && activeRecord.touchActive,
'Cannot find single active touch.',
);
}
}
}

View File

@@ -8,6 +8,7 @@
/* eslint valid-typeof: 0 */
import invariant from 'shared/invariant';
import warningWithoutStack from 'shared/warningWithoutStack';
const EVENT_POOL_SIZE = 10;
@@ -282,17 +283,17 @@ function getPooledWarningPropertyDefinition(propName, getVal) {
}
function warn(action, result) {
if (__DEV__) {
console.error(
"This synthetic event is reused for performance reasons. If you're seeing this, " +
"you're %s `%s` on a released/nullified synthetic event. %s. " +
'If you must keep the original synthetic event around, use event.persist(). ' +
'See https://fb.me/react-event-pooling for more information.',
action,
propName,
result,
);
}
const warningCondition = false;
warningWithoutStack(
warningCondition,
"This synthetic event is reused for performance reasons. If you're seeing this, " +
"you're %s `%s` on a released/nullified synthetic event. %s. " +
'If you must keep the original synthetic event around, use event.persist(). ' +
'See https://fb.me/react-event-pooling for more information.',
action,
propName,
result,
);
}
}

View File

@@ -79,12 +79,12 @@ const _touchConfig = function(
topType === 'topTouchStart'
? allTouchObjects
: topType === 'topTouchMove'
? allTouchObjects
: topType === 'topTouchEnd'
? antiSubsequence(allTouchObjects, changedIndices)
: topType === 'topTouchCancel'
? antiSubsequence(allTouchObjects, changedIndices)
: null;
? allTouchObjects
: topType === 'topTouchEnd'
? antiSubsequence(allTouchObjects, changedIndices)
: topType === 'topTouchCancel'
? antiSubsequence(allTouchObjects, changedIndices)
: null;
return {
nativeEvent: touchEvent(
@@ -314,10 +314,10 @@ const run = function(config, hierarchyConfig, nativeEventConfig) {
// Trigger the event
const extractedEvents = ResponderEventPlugin.extractEvents(
nativeEventConfig.topLevelType,
PLUGIN_EVENT_SYSTEM,
nativeEventConfig.targetInst,
nativeEventConfig.nativeEvent,
nativeEventConfig.target,
PLUGIN_EVENT_SYSTEM,
);
// At this point the negotiation events have been dispatched as part of the

View File

@@ -1,7 +1,7 @@
{
"name": "react-art",
"description": "React ART is a JavaScript library for drawing vector graphics using React. It provides declarative and reactive bindings to the ART library. Using the same declarative API you can render the output to either Canvas, SVG or VML (IE8).",
"version": "16.12.0",
"version": "16.9.0",
"main": "index.js",
"repository": {
"type": "git",
@@ -27,7 +27,7 @@
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.18.0"
"scheduler": "^0.15.0"
},
"peerDependencies": {
"react": "^16.0.0"

View File

@@ -426,7 +426,7 @@ export function unhideTextInstance(textInstance, text): void {
// Noop
}
export function DEPRECATED_mountResponderInstance(
export function mountResponderInstance(
responder: ReactEventResponder<any, any>,
responderInstance: ReactEventResponderInstance<any, any>,
props: Object,
@@ -436,7 +436,7 @@ export function DEPRECATED_mountResponderInstance(
throw new Error('Not yet implemented.');
}
export function DEPRECATED_unmountResponderInstance(
export function unmountResponderInstance(
responderInstance: ReactEventResponderInstance<any, any>,
): void {
throw new Error('Not yet implemented.');
@@ -461,11 +461,3 @@ export function updateFundamentalComponent(fundamentalInstance) {
export function unmountFundamentalComponent(fundamentalInstance) {
throw new Error('Not yet implemented.');
}
export function getInstanceFromNode(node) {
throw new Error('Not yet implemented.');
}
export function beforeRemoveInstance(instance) {
// noop
}

View File

@@ -214,9 +214,7 @@ describe('ReactART', () => {
const chars = this.props.chars.split('');
return (
<Surface>
{chars.map(text => (
<Shape key={text} title={text} />
))}
{chars.map(text => <Shape key={text} title={text} />)}
</Surface>
);
}
@@ -422,7 +420,7 @@ describe('ReactARTComponents', () => {
ReactTestRenderer.create(
<Circle stroke="green" strokeWidth={3} fill="blue" />,
),
).toErrorDev(
).toWarnDev(
'Warning: Failed prop type: The prop `radius` is marked as required in `Circle`, ' +
'but its value is `undefined`.' +
'\n in Circle (at **)',
@@ -439,7 +437,7 @@ describe('ReactARTComponents', () => {
it('should warn if width/height is missing on a Rectangle component', () => {
expect(() =>
ReactTestRenderer.create(<Rectangle stroke="green" fill="blue" />),
).toErrorDev([
).toWarnDev([
'Warning: Failed prop type: The prop `width` is marked as required in `Rectangle`, ' +
'but its value is `undefined`.' +
'\n in Rectangle (at **)',
@@ -464,7 +462,7 @@ describe('ReactARTComponents', () => {
});
it('should warn if outerRadius/startAngle/endAngle is missing on a Wedge component', () => {
expect(() => ReactTestRenderer.create(<Wedge fill="blue" />)).toErrorDev([
expect(() => ReactTestRenderer.create(<Wedge fill="blue" />)).toWarnDev([
'Warning: Failed prop type: The prop `outerRadius` is marked as required in `Wedge`, ' +
'but its value is `undefined`.' +
'\n in Wedge (at **)',

View File

@@ -8,15 +8,17 @@
*/
import React from 'react';
import warningWithoutStack from 'shared/warningWithoutStack';
import {createLRU} from './LRU';
type Thenable<T> = {
then(resolve: (T) => mixed, reject: (mixed) => mixed): mixed,
...
};
type Suspender = {then(resolve: () => mixed, reject: () => mixed): mixed, ...};
type Suspender = {
then(resolve: () => mixed, reject: () => mixed): mixed,
};
type PendingResult = {|
status: 0,
@@ -38,7 +40,6 @@ type Result<V> = PendingResult | ResolvedResult<V> | RejectedResult;
type Resource<I, V> = {
read(I): V,
preload(I): void,
...
};
const Pending = 0;
@@ -63,21 +64,18 @@ function readContext(Context, observedBits) {
function identityHashFn(input) {
if (__DEV__) {
if (
typeof input !== 'string' &&
typeof input !== 'number' &&
typeof input !== 'boolean' &&
input !== undefined &&
input !== null
) {
console.error(
'Invalid key type. Expected a string, number, symbol, or boolean, ' +
'but instead received: %s' +
'\n\nTo use non-primitive values as keys, you must pass a hash ' +
'function as the second argument to createResource().',
input,
);
}
warningWithoutStack(
typeof input === 'string' ||
typeof input === 'number' ||
typeof input === 'boolean' ||
input === undefined ||
input === null,
'Invalid key type. Expected a string, number, symbol, or boolean, ' +
'but instead received: %s' +
'\n\nTo use non-primitive values as keys, you must pass a hash ' +
'function as the second argument to createResource().',
input,
);
}
return input;
}

View File

@@ -33,53 +33,46 @@ describe('ReactCache', () => {
ReactTestRenderer = require('react-test-renderer');
Scheduler = require('scheduler');
TextResource = createResource(
([text, ms = 0]) => {
let listeners = null;
let status = 'pending';
let value = null;
return {
then(resolve, reject) {
switch (status) {
case 'pending': {
if (listeners === null) {
listeners = [{resolve, reject}];
setTimeout(() => {
if (textResourceShouldFail) {
Scheduler.unstable_yieldValue(
`Promise rejected [${text}]`,
);
status = 'rejected';
value = new Error('Failed to load: ' + text);
listeners.forEach(listener => listener.reject(value));
} else {
Scheduler.unstable_yieldValue(
`Promise resolved [${text}]`,
);
status = 'resolved';
value = text;
listeners.forEach(listener => listener.resolve(value));
}
}, ms);
} else {
listeners.push({resolve, reject});
}
break;
}
case 'resolved': {
resolve(value);
break;
}
case 'rejected': {
reject(value);
break;
TextResource = createResource(([text, ms = 0]) => {
let listeners = null;
let status = 'pending';
let value = null;
return {
then(resolve, reject) {
switch (status) {
case 'pending': {
if (listeners === null) {
listeners = [{resolve, reject}];
setTimeout(() => {
if (textResourceShouldFail) {
Scheduler.unstable_yieldValue(`Promise rejected [${text}]`);
status = 'rejected';
value = new Error('Failed to load: ' + text);
listeners.forEach(listener => listener.reject(value));
} else {
Scheduler.unstable_yieldValue(`Promise resolved [${text}]`);
status = 'resolved';
value = text;
listeners.forEach(listener => listener.resolve(value));
}
}, ms);
} else {
listeners.push({resolve, reject});
}
break;
}
},
};
},
([text, ms]) => text,
);
case 'resolved': {
resolve(value);
break;
}
case 'rejected': {
reject(value);
break;
}
}
},
};
}, ([text, ms]) => text);
textResourceShouldFail = false;
});
@@ -179,12 +172,15 @@ describe('ReactCache', () => {
if (__DEV__) {
expect(() => {
expect(Scheduler).toFlushAndYield(['App', 'Loading...']);
}).toErrorDev([
'Invalid key type. Expected a string, number, symbol, or ' +
'boolean, but instead received: Hi,100\n\n' +
'To use non-primitive values as keys, you must pass a hash ' +
'function as the second argument to createResource().',
]);
}).toWarnDev(
[
'Invalid key type. Expected a string, number, symbol, or ' +
'boolean, but instead received: Hi,100\n\n' +
'To use non-primitive values as keys, you must pass a hash ' +
'function as the second argument to createResource().',
],
{withoutStack: true},
);
} else {
expect(Scheduler).toFlushAndYield(['App', 'Loading...']);
}
@@ -300,29 +296,26 @@ describe('ReactCache', () => {
it('if a thenable resolves multiple times, does not update the first cached value', () => {
let resolveThenable;
const BadTextResource = createResource(
([text, ms = 0]) => {
let listeners = null;
let value = null;
return {
then(resolve, reject) {
if (value !== null) {
resolve(value);
const BadTextResource = createResource(([text, ms = 0]) => {
let listeners = null;
let value = null;
return {
then(resolve, reject) {
if (value !== null) {
resolve(value);
} else {
if (listeners === null) {
listeners = [resolve];
resolveThenable = v => {
listeners.forEach(listener => listener(v));
};
} else {
if (listeners === null) {
listeners = [resolve];
resolveThenable = v => {
listeners.forEach(listener => listener(v));
};
} else {
listeners.push(resolve);
}
listeners.push(resolve);
}
},
};
},
([text, ms]) => text,
);
}
},
};
}, ([text, ms]) => text);
function BadAsyncText(props) {
const text = props.text;

View File

@@ -14,9 +14,8 @@ import type {
ReactEventResponderListener,
} from 'shared/ReactTypes';
import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {Hook, TimeoutConfig} from 'react-reconciler/src/ReactFiberHooks';
import type {Hook} from 'react-reconciler/src/ReactFiberHooks';
import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactFiberHooks';
import type {SuspenseConfig} from 'react-reconciler/src/ReactFiberSuspenseConfig';
import ErrorStackParser from 'error-stack-parser';
import ReactSharedInternals from 'shared/ReactSharedInternals';
@@ -25,7 +24,6 @@ import {
SimpleMemoComponent,
ContextProvider,
ForwardRef,
Chunk,
} from 'shared/ReactWorkTags';
type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
@@ -36,7 +34,6 @@ type HookLogEntry = {
primitive: string,
stackError: Error,
value: mixed,
...
};
let hookLog: Array<HookLogEntry> = [];
@@ -117,8 +114,8 @@ function useState<S>(
hook !== null
? hook.memoizedState
: typeof initialState === 'function'
? initialState()
: initialState;
? initialState()
: initialState;
hookLog.push({primitive: 'State', stackError: new Error(), value: state});
return [state, (action: BasicStateAction<S>) => {}];
}
@@ -143,7 +140,7 @@ function useReducer<S, I, A>(
return [state, (action: A) => {}];
}
function useRef<T>(initialValue: T): {|current: T|} {
function useRef<T>(initialValue: T): {current: T} {
let hook = nextHook();
let ref = hook !== null ? hook.memoizedState : {current: initialValue};
hookLog.push({
@@ -175,7 +172,7 @@ function useEffect(
}
function useImperativeHandle<T>(
ref: {|current: T | null|} | ((inst: T | null) => mixed) | null | void,
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
create: () => T,
inputs: Array<mixed> | void | null,
): void {
@@ -239,28 +236,6 @@ function useResponder(
};
}
function useTransition(
config: SuspenseConfig | null | void,
): [(() => void) => void, boolean] {
nextHook();
hookLog.push({
primitive: 'Transition',
stackError: new Error(),
value: config,
});
return [callback => {}, false];
}
function useDeferredValue<T>(value: T, config: TimeoutConfig | null | void): T {
nextHook();
hookLog.push({
primitive: 'DeferredValue',
stackError: new Error(),
value,
});
return value;
}
const Dispatcher: DispatcherType = {
readContext,
useCallback,
@@ -274,8 +249,6 @@ const Dispatcher: DispatcherType = {
useRef,
useState,
useResponder,
useTransition,
useDeferredValue,
};
// Inspect
@@ -286,7 +259,6 @@ export type HooksNode = {
name: string,
value: mixed,
subHooks: Array<HooksNode>,
...
};
export type HooksTree = Array<HooksNode>;
@@ -626,8 +598,7 @@ export function inspectHooksOfFiber(
if (
fiber.tag !== FunctionComponent &&
fiber.tag !== SimpleMemoComponent &&
fiber.tag !== ForwardRef &&
fiber.tag !== Chunk
fiber.tag !== ForwardRef
) {
throw new Error(
'Unknown Fiber. Needs to be a function component to inspect hooks.',

View File

@@ -180,7 +180,7 @@ describe('React hooks DevTools integration', () => {
}
});
it('should support overriding suspense in legacy mode', () => {
it('should support overriding suspense in sync mode', () => {
if (__DEV__) {
// Lock the first render
setSuspenseHandler(() => true);

View File

@@ -17,19 +17,19 @@ describe('ReactHooksInspection', () => {
beforeEach(() => {
jest.resetModules();
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.enableDeprecatedFlareAPI = true;
ReactFeatureFlags.enableFlareAPI = true;
React = require('react');
ReactDebugTools = require('react-debug-tools');
});
it('should inspect a simple useResponder hook', () => {
const TestResponder = React.DEPRECATED_createResponder('TestResponder', {});
const TestResponder = React.unstable_createResponder('TestResponder', {});
function Foo(props) {
const listener = React.DEPRECATED_useResponder(TestResponder, {
const listener = React.unstable_useResponder(TestResponder, {
preventDefault: false,
});
return <div DEPRECATED_flareListeners={listener}>Hello world</div>;
return <div listeners={listener}>Hello world</div>;
}
let tree = ReactDebugTools.inspectHooks(Foo, {});
expect(tree).toEqual([

View File

@@ -1,6 +1,6 @@
{
"name": "react-devtools-core",
"version": "4.4.0",
"version": "4.1.0",
"description": "Use react-devtools outside of the browser",
"license": "MIT",
"main": "./dist/backend.js",

Some files were not shown because too many files have changed in this diff Show More