Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11565a207c | ||
|
|
f2e2637c8e | ||
|
|
1107b9673c | ||
|
|
0975ea3278 | ||
|
|
d827445a50 | ||
|
|
bc9818f24d | ||
|
|
b5398a9130 | ||
|
|
ce6ecd3fbf | ||
|
|
008a2ab9cd | ||
|
|
d1326f466a | ||
|
|
267ed98146 | ||
|
|
fb3f7bfde9 | ||
|
|
81470a0027 | ||
|
|
e602b5291c | ||
|
|
e489c3f9c1 | ||
|
|
c21c41ecfa | ||
|
|
fec00a869c | ||
|
|
66eb293742 | ||
|
|
51c07912ac | ||
|
|
70d4075832 | ||
|
|
ba6477aa3c | ||
|
|
cb1ff430e8 | ||
|
|
9d483dcfd6 | ||
|
|
6d5ea54eee | ||
|
|
e19c9e1064 | ||
|
|
f11a9c1cb0 | ||
|
|
e679a4b6e2 | ||
|
|
8bcc88f2e7 | ||
|
|
6cb26774e2 | ||
|
|
73962c3664 | ||
|
|
9944392285 | ||
|
|
f0befae657 | ||
|
|
a129259ad6 | ||
|
|
c068d31cca | ||
|
|
db695c4d32 | ||
|
|
38247cba36 | ||
|
|
3f0bcaf0db | ||
|
|
ecd919a2f9 | ||
|
|
3fbebb2a0b | ||
|
|
5fce6488ce | ||
|
|
fe2ecd276e | ||
|
|
8f45a7fdc4 | ||
|
|
1fcbd22431 | ||
|
|
2a084f51a9 | ||
|
|
b5a3df6e88 | ||
|
|
9c146e6751 | ||
|
|
baa6d40fc8 | ||
|
|
a1414e8949 | ||
|
|
10a7a5b5ce | ||
|
|
a2fa6eb98d | ||
|
|
9120f6c2d8 | ||
|
|
b66e6e41e6 | ||
|
|
8c1614a2fd | ||
|
|
177fb76353 | ||
|
|
d17d0b99c1 | ||
|
|
4f332885a2 | ||
|
|
e1cd83e49d | ||
|
|
be457ca685 | ||
|
|
17d70df919 | ||
|
|
4846809370 | ||
|
|
4feab7fc92 | ||
|
|
1454a8be03 | ||
|
|
71b64d5211 | ||
|
|
790c8ef041 | ||
|
|
8a120095bf | ||
|
|
7ab8a8e979 | ||
|
|
4392e3821d | ||
|
|
153a0b5980 | ||
|
|
0ca628b216 | ||
|
|
7ad9806d11 | ||
|
|
0fc1547513 | ||
|
|
edb1f59564 | ||
|
|
3e15b1c690 | ||
|
|
0005d1e3f5 | ||
|
|
f290138d32 | ||
|
|
b4ad8e9471 | ||
|
|
ab03e3d651 | ||
|
|
19ef0ec116 | ||
|
|
a9b035b0c2 | ||
|
|
547e059f0b | ||
|
|
3494ee57e6 | ||
|
|
fef40c061e | ||
|
|
659c13963e | ||
|
|
84b86471ea | ||
|
|
c695b2384f | ||
|
|
1c5aa2f23a | ||
|
|
653bc582f9 | ||
|
|
8bfef0da55 | ||
|
|
4a1072194f | ||
|
|
535804f5c8 | ||
|
|
2743fb7b23 | ||
|
|
7325ebe4d6 | ||
|
|
a22880e5e5 | ||
|
|
947bddd5cf | ||
|
|
ce43a8cd07 | ||
|
|
f64906fba1 | ||
|
|
8df4d59be5 | ||
|
|
5bb4ad7376 | ||
|
|
98eb5ae531 | ||
|
|
39489e7674 | ||
|
|
1dc108e582 | ||
|
|
d9871729c8 | ||
|
|
6bf5e85986 | ||
|
|
7a48c900b7 | ||
|
|
e382b0ba95 | ||
|
|
8482cbe22d | ||
|
|
f00c2755b9 | ||
|
|
52bea95cfc | ||
|
|
1d25aa5787 | ||
|
|
f1bf281605 | ||
|
|
16e120438c | ||
|
|
d14ba87b1b | ||
|
|
88ada98198 | ||
|
|
4f964f09c1 | ||
|
|
c2a2d8a539 | ||
|
|
48f1e5b3ce | ||
|
|
f93f3402f7 | ||
|
|
ee3ef3a079 | ||
|
|
33f6f5e532 | ||
|
|
409066a0a1 | ||
|
|
a7f270c550 | ||
|
|
ed4c4a51cd | ||
|
|
686f1060ad | ||
|
|
7475120ce7 | ||
|
|
0c7189d923 | ||
|
|
14be29b2b9 | ||
|
|
dc0dd4bbff | ||
|
|
dd8205cef9 | ||
|
|
1da310809e | ||
|
|
a9fdf8a326 | ||
|
|
327cf0ee33 | ||
|
|
c954efa70f | ||
|
|
ccb14e270c | ||
|
|
0e9cb3f5d0 | ||
|
|
592676503c | ||
|
|
9b2fb24f99 | ||
|
|
a22fabc2a1 | ||
|
|
21d5f7d32d | ||
|
|
5f06576f51 | ||
|
|
8feeed10d8 | ||
|
|
5bce0ef10a | ||
|
|
d7fd679a31 | ||
|
|
3cd89daed9 | ||
|
|
85f5a81eb7 | ||
|
|
f55795c8ee | ||
|
|
d204747bef | ||
|
|
b98adb648a | ||
|
|
f8bfd58680 | ||
|
|
961eb65b4b | ||
|
|
1a6ab1e9b5 | ||
|
|
8ae867e6b5 |
@@ -40,3 +40,9 @@ jobs:
|
||||
key: v1-node-{{ arch }}-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
- store_artifacts:
|
||||
path: ./node_modules.tgz
|
||||
|
||||
- store_artifacts:
|
||||
path: ./scripts/error-codes/codes.json
|
||||
70
CHANGELOG.md
70
CHANGELOG.md
@@ -6,12 +6,78 @@
|
||||
</summary>
|
||||
</details>
|
||||
|
||||
## 16.8.0 (February 6, 2019)
|
||||
|
||||
### React
|
||||
|
||||
* Add [Hooks](https://reactjs.org/docs/hooks-intro.html) — a way to use state and other React features without writing a class. ([@acdlite](https://github.com/acdlite) et al. in [#13968](https://github.com/facebook/react/pull/13968))
|
||||
* Improve the `useReducer` Hook lazy initialization API. ([@acdlite](https://github.com/acdlite) in [#14723](https://github.com/facebook/react/pull/14723))
|
||||
|
||||
### React DOM
|
||||
|
||||
* Bail out of rendering on identical values for `useState` and `useReducer` Hooks. ([@acdlite](https://github.com/acdlite) in [#14569](https://github.com/facebook/react/pull/14569))
|
||||
* Use `Object.is` algorithm for comparing `useState` and `useReducer` values. ([@Jessidhia](https://github.com/Jessidhia) in [#14752](https://github.com/facebook/react/pull/14752))
|
||||
* Don’t compare the first argument passed to `useEffect`/`useMemo`/`useCallback` Hooks. ([@acdlite](https://github.com/acdlite) in [#14594](https://github.com/facebook/react/pull/14594))
|
||||
* Support synchronous thenables passed to `React.lazy()`. ([@gaearon](https://github.com/gaearon) in [#14626](https://github.com/facebook/react/pull/14626))
|
||||
* Render components with Hooks twice in Strict Mode (DEV-only) to match class behavior. ([@gaearon](https://github.com/gaearon) in [#14654](https://github.com/facebook/react/pull/14654))
|
||||
* Warn about mismatching Hook order in development. ([@threepointone](https://github.com/threepointone) in [#14585](https://github.com/facebook/react/pull/14585) and [@acdlite](https://github.com/acdlite) in [#14591](https://github.com/facebook/react/pull/14591))
|
||||
* Effect clean-up functions must return either `undefined` or a function. All other values, including `null`, are not allowed. [@acdlite](https://github.com/acdlite) in [#14119](https://github.com/facebook/react/pull/14119)
|
||||
|
||||
### React Test Renderer and Test Utils
|
||||
|
||||
* Support Hooks in the shallow renderer. ([@trueadm](https://github.com/trueadm) in [#14567](https://github.com/facebook/react/pull/14567))
|
||||
* Fix wrong state in `shouldComponentUpdate` in the presence of `getDerivedStateFromProps` for Shallow Renderer. ([@chenesan](https://github.com/chenesan) in [#14613](https://github.com/facebook/react/pull/14613))
|
||||
* Add `ReactTestRenderer.act()` and `ReactTestUtils.act()` for batching updates so that tests more closely match real behavior. ([@threepointone](https://github.com/threepointone) in [#14744](https://github.com/facebook/react/pull/14744))
|
||||
|
||||
|
||||
### ESLint Plugin: React Hooks
|
||||
|
||||
* Initial [release](https://www.npmjs.com/package/eslint-plugin-react-hooks). ([@calebmer](https://github.com/calebmer) in [#13968](https://github.com/facebook/react/pull/13968))
|
||||
* Fix reporting after encountering a loop. ([@calebmer](https://github.com/calebmer) and [@Yurickh](https://github.com/Yurickh) in [#14661](https://github.com/facebook/react/pull/14661))
|
||||
* Don't consider throwing to be a rule violation. ([@sophiebits](https://github.com/sophiebits) in [#14040](https://github.com/facebook/react/pull/14040))
|
||||
|
||||
## 16.7.0 (December 19, 2018)
|
||||
|
||||
### React DOM
|
||||
|
||||
* Fix performance of `React.lazy` for large numbers of lazily-loaded components. ([@acdlite](http://github.com/acdlite) in [#14429](https://github.com/facebook/react/pull/14429))
|
||||
* Clear fields on unmount to avoid memory leaks. ([@trueadm](http://github.com/trueadm) in [#14276](https://github.com/facebook/react/pull/14276))
|
||||
* Fix bug with SSR and context when mixing `react-dom/server@16.6` and `react@<16.6`. ([@gaearon](http://github.com/gaearon) in [#14291](https://github.com/facebook/react/pull/14291))
|
||||
* Fix a performance regression in profiling mode. ([@bvaughn](http://github.com/bvaughn) in [#14383](https://github.com/facebook/react/pull/14383))
|
||||
|
||||
### Scheduler (Experimental)
|
||||
|
||||
* Post to MessageChannel instead of window. ([@acdlite](http://github.com/acdlite) in [#14234](https://github.com/facebook/react/pull/14234))
|
||||
* Reduce serialization overhead. ([@developit](http://github.com/developit) in [#14249](https://github.com/facebook/react/pull/14249))
|
||||
* Fix fallback to `setTimeout` in testing environments. ([@bvaughn](http://github.com/bvaughn) in [#14358](https://github.com/facebook/react/pull/14358))
|
||||
* Add methods for debugging. ([@mrkev](http://github.com/mrkev) in [#14053](https://github.com/facebook/react/pull/14053))
|
||||
|
||||
|
||||
## 16.6.3 (November 12, 2018)
|
||||
|
||||
### React DOM
|
||||
|
||||
* Fix bugs in `Suspense` and `lazy`. ([@acdlite](https://github.com/acdlite) in [#14133](https://github.com/facebook/react/pull/14133), [#14157](https://github.com/facebook/react/pull/14157), and [#14164](https://github.com/facebook/react/pull/14164))
|
||||
* Fix highlighting of `React.memo` updates in React DevTools. ([@bvaughn](https://github.com/bvaughn) in [#14141](https://github.com/facebook/react/pull/14141))
|
||||
* Fix interaction of Suspense with the React Profiler. ([@bvaughn](https://github.com/bvaughn) in [#14065](https://github.com/facebook/react/pull/14065))
|
||||
* Fix a false positive warning when using Suspense. ([@acdlite](https://github.com/acdlite) in [#14158](https://github.com/facebook/react/pull/14158))
|
||||
|
||||
### React DOM Server
|
||||
|
||||
* Fix incorrect sharing of context state between `renderToNodeStream()` calls. ([@sebmarkbage](https://github.com/sebmarkbage) in [#14182](https://github.com/facebook/react/pull/14182))
|
||||
* Add a warning about incorrect usage of the context API. ([@trueadm](https://github.com/trueadm) in [#14033](https://github.com/facebook/react/pull/14033))
|
||||
|
||||
## 16.6.2 (November 12, 2018)
|
||||
|
||||
This release was published in a broken state and should be skipped.
|
||||
|
||||
## 16.6.1 (November 6, 2018)
|
||||
|
||||
### React DOM
|
||||
|
||||
* Fallback should not remount every time a promise resolves. ([@acdlite](https://github.com/acdlite) in [#14083](https://github.com/facebook/react/pull/14083))
|
||||
* Fix bug where Suspense keeps showing fallback even after everything finishes loading. ([@acdlite](https://github.com/acdlite) in [#14083](https://github.com/facebook/react/pull/14083))
|
||||
* Fix a crash when Suspense finishes loading in IE11. ([@sophiebits](https://github.com/sophiebits) in [#14126](https://github.com/facebook/react/pull/14126))
|
||||
* Fix unresolved default props in lifecycle methods of a lazy component. ([@gaearon](https://github.com/gaearon) in [#14112](https://github.com/facebook/react/pull/14112))
|
||||
* Fix bug when recovering from an error thrown during complete phase. ([@gaearon](https://github.com/gaearon) in [#14104](https://github.com/facebook/react/pull/14104))
|
||||
|
||||
@@ -107,7 +173,7 @@
|
||||
* Fix incorrect data in `compositionend` event when typing Korean on IE11 ([@crux153](https://github.com/crux153) in [#12563](https://github.com/facebook/react/issues/12563))
|
||||
* Fix a crash when using dynamic `children` in the `<option>` tag ([@Slowyn](https://github.com/Slowyn) in [#13261](https://github.com/facebook/react/issues/13261), [@gaearon](https://github.com/gaearon) in [#13465](https://github.com/facebook/react/pull/13465))
|
||||
* Fix the `checked` attribute not getting initially set on the `input` ([@dilidili](https://github.com/dilidili) in [#13114](https://github.com/facebook/react/issues/13114))
|
||||
* Fix hydration of `dangerouslySetInnerHTML` when `__html` is not a string ([@gaearon](https://github.com/gaearon) in [#13353](https://github.com/facebook/react/issues/13353))
|
||||
* Fix hydration of `dangerouslySetInnerHTML` when `__html` is not a string ([@gaearon](https://github.com/gaearon) in [#13353](https://github.com/facebook/react/issues/13353))
|
||||
* Fix a warning about missing controlled `onChange` to fire on falsy values too ([@nicolevy](https://github.com/nicolevy) in [#12628](https://github.com/facebook/react/issues/12628))
|
||||
* Fix `submit` and `reset` buttons getting an empty label ([@ellsclytn](https://github.com/ellsclytn) in [#12780](https://github.com/facebook/react/issues/12780))
|
||||
* Fix the `onSelect` event not being triggered after drag and drop ([@gaearon](https://github.com/gaearon) in [#13422](https://github.com/facebook/react/issues/13422))
|
||||
@@ -298,7 +364,7 @@
|
||||
* Deduplicate warning messages about invalid callback. ([@yenshih](https://github.com/yenshih) in [#11833](https://github.com/facebook/react/pull/11833))
|
||||
* Deprecate `ReactDOM.unstable_createPortal()` in favor of `ReactDOM.createPortal()`. ([@prometheansacrifice](https://github.com/prometheansacrifice) in [#11747](https://github.com/facebook/react/pull/11747))
|
||||
* Don't emit User Timing entries for context types. ([@abhaynikam](https://github.com/abhaynikam) in [#12250](https://github.com/facebook/react/pull/12250))
|
||||
* Improve the error message when context consumer child isn't a function. ([@raunofreiberg](https://github.com/raunofreiberg) in [#12267](https://github.com/facebook/react/pull/12267))
|
||||
* Improve the error message when context consumer child isn't a function. ([@raunofreiberg](https://github.com/raunofreiberg) in [#12267](https://github.com/facebook/react/pull/12267))
|
||||
* Improve the error message when adding a ref to a functional component. ([@skiritsis](https://github.com/skiritsis) in [#11782](https://github.com/facebook/react/pull/11782))
|
||||
|
||||
### React DOM Server
|
||||
|
||||
@@ -60,9 +60,9 @@ You'll notice that we used an HTML-like syntax; [we call it JSX](https://reactjs
|
||||
|
||||
The main purpose of this repository is to continue to evolve React core, making it faster and easier to use. Development of React happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving React.
|
||||
|
||||
### [Code of Conduct](https://code.facebook.com/codeofconduct)
|
||||
### [Code of Conduct](https://code.fb.com/codeofconduct)
|
||||
|
||||
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
|
||||
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.fb.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
|
||||
|
||||
### [Contributing Guide](https://reactjs.org/contributing/how-to-contribute.html)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//
|
||||
// You'll need a GitHub token, you can re-use this one:
|
||||
//
|
||||
// e622517d9f1136ea8900 07c6373666312cdfaa69
|
||||
// 0a7d5c3cad9a6dbec2d9 9a5222cf49062a4c1ef7
|
||||
//
|
||||
// (Just remove the space)
|
||||
//
|
||||
@@ -29,9 +29,16 @@ const {markdown, danger} = require('danger');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
const {generateResultsArray} = require('./scripts/rollup/stats');
|
||||
const {readFileSync} = require('fs');
|
||||
const {existsSync, readFileSync} = require('fs');
|
||||
const {exec} = require('child_process');
|
||||
|
||||
if (!existsSync('./scripts/rollup/results.json')) {
|
||||
// This indicates the build failed previously.
|
||||
// In that case, there's nothing for the Dangerfile to do.
|
||||
// Exit early to avoid leaving a redundant (and potentially confusing) PR comment.
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const currentBuildResults = JSON.parse(
|
||||
readFileSync('./scripts/rollup/results.json')
|
||||
);
|
||||
|
||||
@@ -32,7 +32,7 @@ class VectorWidget extends React.Component {
|
||||
/**
|
||||
* When the component is mounted into the document - this is similar to a
|
||||
* constructor, but invoked when the instance is actually mounted into the
|
||||
* document. Here's, we'll just set up an animation loop that invokes our
|
||||
* document. Here, we'll just set up an animation loop that invokes our
|
||||
* method. Binding of `this.onTick` is not needed because all React methods
|
||||
* are automatically bound before being mounted.
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"prestart":
|
||||
"cp ../../build/dist/react.development.js public/ && cp ../../build/dist/react-dom.development.js public/ && cp ../../build/dist/react-dom-server.browser.development.js public/",
|
||||
"cp ../../build/node_modules/react/umd/react.development.js public/ && cp ../../build/node_modules/react-dom/umd/react-dom.development.js public/ && cp ../../build/node_modules/react-dom/umd/react-dom-server.browser.development.js public/",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
|
||||
3
fixtures/dom/.gitignore
vendored
3
fixtures/dom/.gitignore
vendored
@@ -9,8 +9,11 @@ coverage
|
||||
# production
|
||||
build
|
||||
public/react.development.js
|
||||
public/react.production.min.js
|
||||
public/react-dom.development.js
|
||||
public/react-dom.production.min.js
|
||||
public/react-dom-server.browser.development.js
|
||||
public/react-dom-server.browser.production.min.js
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"prestart": "cp ../../build/dist/react.development.js ../../build/dist/react-dom.development.js ../../build/dist/react-dom-server.browser.development.js public/",
|
||||
"prestart": "cp ../../build/node_modules/react/umd/react.development.js ../../build/node_modules/react-dom/umd/react-dom.development.js ../../build/node_modules/react/umd/react.production.min.js ../../build/node_modules/react-dom/umd/react-dom.production.min.js ../../build/node_modules/react-dom/umd/react-dom-server.browser.development.js ../../build/node_modules/react-dom/umd/react-dom-server.browser.production.min.js public/",
|
||||
"build": "react-scripts build && cp build/index.html build/200.html",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
|
||||
@@ -7,8 +7,9 @@ class Header extends React.Component {
|
||||
super(props, context);
|
||||
const query = parse(window.location.search);
|
||||
const version = query.version || 'local';
|
||||
const production = query.production || false;
|
||||
const versions = [version];
|
||||
this.state = {version, versions};
|
||||
this.state = {version, versions, production};
|
||||
}
|
||||
componentWillMount() {
|
||||
getVersionTags().then(tags => {
|
||||
@@ -25,6 +26,14 @@ class Header extends React.Component {
|
||||
}
|
||||
window.location.search = stringify(query);
|
||||
}
|
||||
handleProductionChange(event) {
|
||||
const query = parse(window.location.search);
|
||||
query.production = event.target.checked;
|
||||
if (!query.production) {
|
||||
delete query.production;
|
||||
}
|
||||
window.location.search = stringify(query);
|
||||
}
|
||||
handleFixtureChange(event) {
|
||||
window.location.pathname = event.target.value;
|
||||
}
|
||||
@@ -43,6 +52,16 @@ class Header extends React.Component {
|
||||
</span>
|
||||
|
||||
<div className="header-controls">
|
||||
<input
|
||||
id="react_production"
|
||||
className="header__checkbox"
|
||||
type="checkbox"
|
||||
checked={this.state.production}
|
||||
onChange={this.handleProductionChange}
|
||||
/>
|
||||
<label htmlFor="react_production" className="header__label">
|
||||
Production
|
||||
</label>
|
||||
<label htmlFor="example">
|
||||
<span className="sr-only">Select an example</span>
|
||||
<select
|
||||
|
||||
36
fixtures/dom/src/react-loader.js
vendored
36
fixtures/dom/src/react-loader.js
vendored
@@ -37,12 +37,15 @@ function loadScript(src) {
|
||||
}
|
||||
|
||||
export function reactPaths() {
|
||||
let reactPath = 'react.development.js';
|
||||
let reactDOMPath = 'react-dom.development.js';
|
||||
let reactDOMServerPath = 'react-dom-server.browser.development.js';
|
||||
|
||||
let query = parseQuery(window.location.search);
|
||||
let version = query.version || 'local';
|
||||
let isProduction = query.production === 'true';
|
||||
|
||||
let environment = isProduction ? 'production.min' : 'development';
|
||||
|
||||
let reactPath = 'react.' + environment + '.js';
|
||||
let reactDOMPath = 'react-dom.' + environment + '.js';
|
||||
let reactDOMServerPath = 'react-dom-server.browser.' + environment + '.js';
|
||||
|
||||
if (version !== 'local') {
|
||||
const {major, minor, prerelease} = semver(version);
|
||||
@@ -51,21 +54,34 @@ export function reactPaths() {
|
||||
// Load the old module location for anything less than 16 RC
|
||||
if (major >= 16 && !(minor === 0 && preReleaseStage === 'alpha')) {
|
||||
reactPath =
|
||||
'https://unpkg.com/react@' + version + '/umd/react.development.js';
|
||||
'https://unpkg.com/react@' +
|
||||
version +
|
||||
'/umd/react.' +
|
||||
environment +
|
||||
'.js';
|
||||
reactDOMPath =
|
||||
'https://unpkg.com/react-dom@' +
|
||||
version +
|
||||
'/umd/react-dom.development.js';
|
||||
'/umd/react-dom.' +
|
||||
environment +
|
||||
'.js';
|
||||
reactDOMServerPath =
|
||||
'https://unpkg.com/react-dom@' +
|
||||
version +
|
||||
'/umd/react-dom-server.browser.development';
|
||||
'/umd/react-dom-server.browser.' +
|
||||
environment +
|
||||
'.js';
|
||||
} else {
|
||||
reactPath = 'https://unpkg.com/react@' + version + '/dist/react.js';
|
||||
let suffix = isProduction ? '.min.js' : '.js';
|
||||
|
||||
reactPath = 'https://unpkg.com/react@' + version + '/dist/react' + suffix;
|
||||
reactDOMPath =
|
||||
'https://unpkg.com/react-dom@' + version + '/dist/react-dom.js';
|
||||
'https://unpkg.com/react-dom@' + version + '/dist/react-dom' + suffix;
|
||||
reactDOMServerPath =
|
||||
'https://unpkg.com/react-dom@' + version + '/dist/react-dom-server.js';
|
||||
'https://unpkg.com/react-dom@' +
|
||||
version +
|
||||
'/dist/react-dom-server' +
|
||||
suffix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,15 @@ textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header__checkbox {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header__label {
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
clip: rect(0, 0, 0, 0);
|
||||
height: 0;
|
||||
|
||||
14
fixtures/eslint/.eslintrc.json
Normal file
14
fixtures/eslint/.eslintrc.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"root": true,
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"plugins": ["react-hooks"],
|
||||
"rules": {
|
||||
"react-hooks/rules-of-hooks": 2
|
||||
}
|
||||
}
|
||||
7
fixtures/eslint/README.md
Normal file
7
fixtures/eslint/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# ESLint Playground Fixture
|
||||
|
||||
This is an internal playground for quick iteration on our lint rules inside an IDE like VSCode.
|
||||
|
||||
See instructions in `./index.js` in this directory.
|
||||
|
||||

|
||||
11
fixtures/eslint/index.js
Normal file
11
fixtures/eslint/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// This is a testing playground for our lint rules.
|
||||
|
||||
// 1. Run yarn && yarn start
|
||||
// 2. "File > Add Folder to Workspace" this specific folder in VSCode with ESLint extension
|
||||
// 3. Changes to the rule source should get picked up without restarting ESLint server
|
||||
|
||||
function Foo() {
|
||||
if (condition) {
|
||||
useEffect(() => {});
|
||||
}
|
||||
}
|
||||
12
fixtures/eslint/package.json
Normal file
12
fixtures/eslint/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "eslint-playground",
|
||||
"dependencies": {
|
||||
"eslint": "4.1.0",
|
||||
"eslint-plugin-react-hooks": "link:./proxy"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "./watch.sh",
|
||||
"lint": "eslint index.js"
|
||||
}
|
||||
}
|
||||
35
fixtures/eslint/proxy/index.js
Normal file
35
fixtures/eslint/proxy/index.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
// This file is a proxy for our rule definition that will
|
||||
// load the latest built version on every check. This makes
|
||||
// it convenient to test inside IDEs (which would otherwise
|
||||
// load a version of our rule once and never restart the server).
|
||||
// See instructions in ../index.js playground.
|
||||
|
||||
let build;
|
||||
reload();
|
||||
|
||||
function reload() {
|
||||
for (let id in require.cache) {
|
||||
if (/eslint-plugin-react-hooks/.test(id)) {
|
||||
delete require.cache[id];
|
||||
}
|
||||
}
|
||||
// Point to the built version.
|
||||
build = require('../../../build/node_modules/eslint-plugin-react-hooks');
|
||||
}
|
||||
|
||||
let rules = {};
|
||||
for (let key in build.rules) {
|
||||
if (build.rules.hasOwnProperty(key)) {
|
||||
rules[key] = Object.assign({}, build.rules, {
|
||||
create() {
|
||||
// Reload changes to the built rule
|
||||
reload();
|
||||
return build.rules[key].create.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {rules};
|
||||
4
fixtures/eslint/proxy/package.json
Normal file
4
fixtures/eslint/proxy/package.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "0.0.0"
|
||||
}
|
||||
3
fixtures/eslint/watch.sh
Executable file
3
fixtures/eslint/watch.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
(cd ../.. && yarn build eslint --type=NODE_DEV)
|
||||
(cd ../.. && watchman-make --make 'yarn build eslint --type=NODE_DEV' -p 'packages/eslint-plugin-*/**/*' -t ignored)
|
||||
853
fixtures/eslint/yarn.lock
Normal file
853
fixtures/eslint/yarn.lock
Normal file
@@ -0,0 +1,853 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
acorn-jsx@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
|
||||
integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=
|
||||
dependencies:
|
||||
acorn "^3.0.4"
|
||||
|
||||
acorn@^3.0.4:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
|
||||
integrity sha1-ReN/s56No/JbruP/U2niu18iAXo=
|
||||
|
||||
acorn@^5.5.0:
|
||||
version "5.7.3"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
|
||||
integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==
|
||||
|
||||
ajv-keywords@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
|
||||
integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=
|
||||
|
||||
ajv@^6.0.1:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96"
|
||||
integrity sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==
|
||||
dependencies:
|
||||
fast-deep-equal "^2.0.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-escapes@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30"
|
||||
integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==
|
||||
|
||||
ansi-regex@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
|
||||
|
||||
ansi-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||
|
||||
ansi-styles@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
|
||||
integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
babel-code-frame@^6.22.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||
integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=
|
||||
dependencies:
|
||||
chalk "^1.1.3"
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.2"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
caller-path@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
|
||||
integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=
|
||||
dependencies:
|
||||
callsites "^0.2.0"
|
||||
|
||||
callsites@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
|
||||
integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=
|
||||
|
||||
chalk@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
|
||||
dependencies:
|
||||
ansi-styles "^2.2.1"
|
||||
escape-string-regexp "^1.0.2"
|
||||
has-ansi "^2.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.1.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chardet@^0.4.0:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
|
||||
integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
|
||||
|
||||
circular-json@^0.3.1:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
|
||||
integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==
|
||||
|
||||
cli-cursor@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
||||
integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
|
||||
dependencies:
|
||||
restore-cursor "^2.0.0"
|
||||
|
||||
cli-width@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
|
||||
integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
concat-stream@^1.6.0:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
debug@^2.6.8:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
deep-is@~0.1.3:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
|
||||
|
||||
doctrine@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
|
||||
integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
"eslint-plugin-react-hooks@link:./proxy":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
eslint-scope@^3.7.1:
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535"
|
||||
integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==
|
||||
dependencies:
|
||||
esrecurse "^4.1.0"
|
||||
estraverse "^4.1.1"
|
||||
|
||||
eslint@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.1.0.tgz#bbb55a28220ee08b69da9554d45a6b2ebfd7d913"
|
||||
integrity sha1-u7VaKCIO4Itp2pVU1FprLr/X2RM=
|
||||
dependencies:
|
||||
babel-code-frame "^6.22.0"
|
||||
chalk "^1.1.3"
|
||||
concat-stream "^1.6.0"
|
||||
debug "^2.6.8"
|
||||
doctrine "^2.0.0"
|
||||
eslint-scope "^3.7.1"
|
||||
espree "^3.4.3"
|
||||
esquery "^1.0.0"
|
||||
estraverse "^4.2.0"
|
||||
esutils "^2.0.2"
|
||||
file-entry-cache "^2.0.0"
|
||||
glob "^7.1.2"
|
||||
globals "^9.17.0"
|
||||
ignore "^3.3.3"
|
||||
imurmurhash "^0.1.4"
|
||||
inquirer "^3.0.6"
|
||||
is-my-json-valid "^2.16.0"
|
||||
is-resolvable "^1.0.0"
|
||||
js-yaml "^3.8.4"
|
||||
json-stable-stringify "^1.0.1"
|
||||
levn "^0.3.0"
|
||||
lodash "^4.17.4"
|
||||
minimatch "^3.0.2"
|
||||
mkdirp "^0.5.1"
|
||||
natural-compare "^1.4.0"
|
||||
optionator "^0.8.2"
|
||||
path-is-inside "^1.0.2"
|
||||
pluralize "^4.0.0"
|
||||
progress "^2.0.0"
|
||||
require-uncached "^1.0.3"
|
||||
strip-json-comments "~2.0.1"
|
||||
table "^4.0.1"
|
||||
text-table "~0.2.0"
|
||||
|
||||
espree@^3.4.3:
|
||||
version "3.5.4"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
|
||||
integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==
|
||||
dependencies:
|
||||
acorn "^5.5.0"
|
||||
acorn-jsx "^3.0.0"
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
esquery@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
|
||||
integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
|
||||
dependencies:
|
||||
estraverse "^4.0.0"
|
||||
|
||||
esrecurse@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
|
||||
integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
|
||||
dependencies:
|
||||
estraverse "^4.1.0"
|
||||
|
||||
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
|
||||
integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
|
||||
integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=
|
||||
|
||||
external-editor@^2.0.4:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
|
||||
integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==
|
||||
dependencies:
|
||||
chardet "^0.4.0"
|
||||
iconv-lite "^0.4.17"
|
||||
tmp "^0.0.33"
|
||||
|
||||
fast-deep-equal@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
|
||||
integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
|
||||
integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
|
||||
|
||||
fast-levenshtein@~2.0.4:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||
|
||||
figures@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
|
||||
integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
|
||||
file-entry-cache@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
|
||||
integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=
|
||||
dependencies:
|
||||
flat-cache "^1.2.1"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
flat-cache@^1.2.1:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f"
|
||||
integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==
|
||||
dependencies:
|
||||
circular-json "^0.3.1"
|
||||
graceful-fs "^4.1.2"
|
||||
rimraf "~2.6.2"
|
||||
write "^0.2.1"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
generate-function@^2.0.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f"
|
||||
integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==
|
||||
dependencies:
|
||||
is-property "^1.0.2"
|
||||
|
||||
generate-object-property@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0"
|
||||
integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=
|
||||
dependencies:
|
||||
is-property "^1.0.0"
|
||||
|
||||
glob@^7.1.2, glob@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
globals@^9.17.0:
|
||||
version "9.18.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
|
||||
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
|
||||
|
||||
graceful-fs@^4.1.2:
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
|
||||
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
|
||||
|
||||
has-ansi@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
||||
integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
has-flag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
iconv-lite@^0.4.17:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
ignore@^3.3.3:
|
||||
version "3.3.10"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||
integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==
|
||||
|
||||
imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@^2.0.3, inherits@~2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
inquirer@^3.0.6:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
|
||||
integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==
|
||||
dependencies:
|
||||
ansi-escapes "^3.0.0"
|
||||
chalk "^2.0.0"
|
||||
cli-cursor "^2.1.0"
|
||||
cli-width "^2.0.0"
|
||||
external-editor "^2.0.4"
|
||||
figures "^2.0.0"
|
||||
lodash "^4.3.0"
|
||||
mute-stream "0.0.7"
|
||||
run-async "^2.2.0"
|
||||
rx-lite "^4.0.8"
|
||||
rx-lite-aggregates "^4.0.8"
|
||||
string-width "^2.1.0"
|
||||
strip-ansi "^4.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
is-fullwidth-code-point@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
|
||||
|
||||
is-my-ip-valid@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824"
|
||||
integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==
|
||||
|
||||
is-my-json-valid@^2.16.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz#8fd6e40363cd06b963fa877d444bfb5eddc62175"
|
||||
integrity sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==
|
||||
dependencies:
|
||||
generate-function "^2.0.0"
|
||||
generate-object-property "^1.1.0"
|
||||
is-my-ip-valid "^1.0.0"
|
||||
jsonpointer "^4.0.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
is-promise@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
|
||||
integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
|
||||
|
||||
is-property@^1.0.0, is-property@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
|
||||
integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=
|
||||
|
||||
is-resolvable@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
||||
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
|
||||
js-tokens@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
|
||||
|
||||
js-yaml@^3.8.4:
|
||||
version "3.12.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600"
|
||||
integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json-stable-stringify@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af"
|
||||
integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=
|
||||
dependencies:
|
||||
jsonify "~0.0.0"
|
||||
|
||||
jsonify@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
|
||||
|
||||
jsonpointer@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
|
||||
integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk=
|
||||
|
||||
levn@^0.3.0, levn@~0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||
integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
|
||||
dependencies:
|
||||
prelude-ls "~1.1.2"
|
||||
type-check "~0.3.2"
|
||||
|
||||
lodash@^4.17.4, lodash@^4.3.0:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
|
||||
mimic-fn@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
|
||||
|
||||
minimatch@^3.0.2, minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
|
||||
mkdirp@^0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
mute-stream@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
object-assign@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
onetime@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
|
||||
integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
|
||||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
optionator@^0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
|
||||
integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=
|
||||
dependencies:
|
||||
deep-is "~0.1.3"
|
||||
fast-levenshtein "~2.0.4"
|
||||
levn "~0.3.0"
|
||||
prelude-ls "~1.1.2"
|
||||
type-check "~0.3.2"
|
||||
wordwrap "~1.0.0"
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-is-inside@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
||||
integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
|
||||
|
||||
pluralize@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762"
|
||||
integrity sha1-WbcIwcAZCi9pLxx2GMRGsFL9F2I=
|
||||
|
||||
prelude-ls@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
|
||||
integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
|
||||
|
||||
progress@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
readable-stream@^2.2.2:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
require-uncached@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
|
||||
integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=
|
||||
dependencies:
|
||||
caller-path "^0.1.0"
|
||||
resolve-from "^1.0.0"
|
||||
|
||||
resolve-from@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
|
||||
integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=
|
||||
|
||||
restore-cursor@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
|
||||
dependencies:
|
||||
onetime "^2.0.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
rimraf@~2.6.2:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
run-async@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
||||
integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA=
|
||||
dependencies:
|
||||
is-promise "^2.1.0"
|
||||
|
||||
rx-lite-aggregates@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
|
||||
integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=
|
||||
dependencies:
|
||||
rx-lite "*"
|
||||
|
||||
rx-lite@*, rx-lite@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
|
||||
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
|
||||
|
||||
slice-ansi@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
|
||||
integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==
|
||||
dependencies:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
string-width@^2.1.0, string-width@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||
dependencies:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^4.0.0"
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
strip-ansi@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
|
||||
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
strip-ansi@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
|
||||
strip-json-comments@~2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||
|
||||
supports-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
table@^4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc"
|
||||
integrity sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==
|
||||
dependencies:
|
||||
ajv "^6.0.1"
|
||||
ajv-keywords "^3.0.0"
|
||||
chalk "^2.1.0"
|
||||
lodash "^4.17.4"
|
||||
slice-ansi "1.0.0"
|
||||
string-width "^2.1.1"
|
||||
|
||||
text-table@~0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
||||
|
||||
through@^2.3.6:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
type-check@~0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
|
||||
integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
|
||||
dependencies:
|
||||
prelude-ls "~1.1.2"
|
||||
|
||||
typedarray@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
wordwrap@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
|
||||
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
write@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
|
||||
integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=
|
||||
dependencies:
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
xtend@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||
integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=
|
||||
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"prestart":
|
||||
"cp ../../build/dist/react.development.js public/ && cp ../../build/dist/react-dom.development.js public/",
|
||||
"cp ../../build/node_modules/react/umd/react.development.js public/ && cp ../../build/node_modules/react-dom/umd/react-dom.development.js public/",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
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/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<script type="text/babel">
|
||||
var dotStyle = {
|
||||
|
||||
40
fixtures/fizz-ssr-browser/index.html
Normal file
40
fixtures/fizz-ssr-browser/index.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html style="width: 100%; height: 100%; overflow: hidden">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Fizz Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Fizz 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-unstable-fizz.browser.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<script type="text/babel">
|
||||
let stream = ReactDOMFizzServer.renderToReadableStream(<body>Success</body>);
|
||||
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 iframe = document.createElement('iframe');
|
||||
iframe.src = url;
|
||||
let container = document.getElementById('container');
|
||||
container.innerHTML = '';
|
||||
container.appendChild(iframe);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -15,28 +15,18 @@ First, build React and the fixtures:
|
||||
```
|
||||
cd react
|
||||
npm run build
|
||||
|
||||
cd fixtures/packaging
|
||||
node build-all.js
|
||||
node fixtures/packaging/build-all.js
|
||||
```
|
||||
|
||||
Then run a local server at the root of the repo, e.g.
|
||||
Then run a local server, e.g.
|
||||
|
||||
```
|
||||
npm i -g pushstate-server
|
||||
cd ../..
|
||||
pushstate-server .
|
||||
npx pushstate-server .
|
||||
```
|
||||
|
||||
(Too complicated? Send a PR to simplify this :-)).
|
||||
|
||||
Then open the following URL in your browser:
|
||||
|
||||
```
|
||||
open http://localhost:9000/fixtures/packaging/index.html
|
||||
```
|
||||
and open the following URL in your browser: [http://localhost:9000/fixtures/packaging/index.html](http://localhost:9000/fixtures/packaging/index.html)
|
||||
|
||||
You should see two things:
|
||||
|
||||
* "Hello World" is rendered in each iframe.
|
||||
* A number of iframes (corresponding to various builds), with "Hello World" rendered in each iframe.
|
||||
* No errors in the console.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<script src="../../../build/dist/react.development.js"></script>
|
||||
<script src="../../../build/dist/react-dom.development.js"></script>
|
||||
<script src="../../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<div id="container"></div>
|
||||
<script type="text/babel">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<script src="../../../build/dist/react.development.js"></script>
|
||||
<script src="../../../build/dist/react-dom.development.js"></script>
|
||||
<script src="../../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
|
||||
<div id="container"></div>
|
||||
<script>
|
||||
ReactDOM.render(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<script src="../../../build/dist/react.production.min.js"></script>
|
||||
<script src="../../../build/dist/react-dom.production.min.js"></script>
|
||||
<script src="../../../build/node_modules/react/umd/react.production.min.js"></script>
|
||||
<script src="../../../build/node_modules/react-dom/umd/react-dom.production.min.js"></script>
|
||||
<div id="container"></div>
|
||||
<script>
|
||||
ReactDOM.render(
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<script>
|
||||
requirejs.config({
|
||||
paths: {
|
||||
react: '../../../build/dist/react.development',
|
||||
'react-dom': '../../../build/dist/react-dom.development'
|
||||
react: '../../../build/node_modules/react/umd/react.development',
|
||||
'react-dom': '../../../build/node_modules/react-dom/umd/react-dom.development'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<script>
|
||||
requirejs.config({
|
||||
paths: {
|
||||
react: '../../../build/dist/react.production.min',
|
||||
'react-dom': '../../../build/dist/react-dom.production.min'
|
||||
react: '../../../build/node_modules/react/umd/react.production.min',
|
||||
'react-dom': '../../../build/node_modules/react-dom/umd/react-dom.production.min'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ module.exports = {
|
||||
out: 'output.js',
|
||||
optimize: 'none',
|
||||
paths: {
|
||||
react: '../../../../build/dist/react.development',
|
||||
'react-dom': '../../../../build/dist/react-dom.development',
|
||||
react: '../../../../build/node_modules/react/umd/react.development',
|
||||
'react-dom':
|
||||
'../../../../build/node_modules/react-dom/umd/react-dom.development',
|
||||
schedule: '../../../../build/dist/schedule.development',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,8 +4,9 @@ module.exports = {
|
||||
out: 'output.js',
|
||||
optimize: 'none',
|
||||
paths: {
|
||||
react: '../../../../build/dist/react.production.min',
|
||||
'react-dom': '../../../../build/dist/react-dom.production.min',
|
||||
react: '../../../../build/node_modules/react/umd/react.production.min',
|
||||
'react-dom':
|
||||
'../../../../build/node_modules/react-dom/umd/react-dom.production.min',
|
||||
schedule: '../../../../build/dist/schedule.development',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
System.config({
|
||||
paths: {
|
||||
react: '../../../../build/dist/react.development.js',
|
||||
'react-dom': '../../../../build/dist/react-dom.development.js',
|
||||
react: '../../../../build/node_modules/react/umd/react.development.js',
|
||||
'react-dom':
|
||||
'../../../../build/node_modules/react-dom/umd/react-dom.development.js',
|
||||
schedule: '../../../../build/dist/schedule.development',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
System.config({
|
||||
paths: {
|
||||
react: '../../../../build/dist/react.production.min.js',
|
||||
'react-dom': '../../../../build/dist/react-dom.production.min.js',
|
||||
react: '../../../../build/node_modules/react/umd/react.production.min.js',
|
||||
'react-dom':
|
||||
'../../../../build/node_modules/react-dom/umd/react-dom.production.min.js',
|
||||
schedule: '../../../../build/dist/schedule.development',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<script>
|
||||
System.config({
|
||||
paths: {
|
||||
react: '../../../build/dist/react.development.js',
|
||||
'react-dom': '../../../build/dist/react-dom.development.js'
|
||||
react: '../../../build/node_modules/react/umd/react.development.js',
|
||||
'react-dom': '../../../build/node_modules/react-dom/umd/react-dom.development.js'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<script>
|
||||
System.config({
|
||||
paths: {
|
||||
react: '../../../build/dist/react.production.min.js',
|
||||
'react-dom': '../../../build/dist/react-dom.production.min.js'
|
||||
react: '../../../build/node_modules/react/umd/react.production.min.js',
|
||||
'react-dom': '../../../build/node_modules/react-dom/umd/react-dom.production.min.js'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -90,15 +90,33 @@
|
||||
</div>
|
||||
<div> If the counter advanced while you were away from this tab, it's correct.</div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Can pause execution, dump scheduled callbacks, and continue where it left off</p>
|
||||
<button onClick="runTestEight()">Run Test 8</button>
|
||||
<div><b>Click the button above, press "continue" to finish the test after it pauses:</b></div>
|
||||
<button onClick="continueTestEight()">continue</button>
|
||||
<div><b>Expected:</b></div>
|
||||
<div id="test-8-expected">
|
||||
</div>
|
||||
<div> -------------------------------------------------</div>
|
||||
<div> If the test didn't progress until you hit "continue" and </div>
|
||||
<div> you see the same above and below afterwards it's correct.
|
||||
<div> -------------------------------------------------</div>
|
||||
<div><b>Actual:</b></div>
|
||||
<div id="test-8"></div>
|
||||
</li>
|
||||
</ol>
|
||||
<script src="../../build/dist/react.development.js"></script>
|
||||
<script src="../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<script type="text/babel">
|
||||
const {
|
||||
unstable_scheduleCallback: scheduleCallback,
|
||||
unstable_cancelCallback: cancelCallback,
|
||||
unstable_now: now
|
||||
unstable_now: now,
|
||||
unstable_getFirstCallbackNode: getFirstCallbackNode,
|
||||
unstable_pauseExecution: pauseExecution,
|
||||
unstable_continueExecution: continueExecution,
|
||||
} = Scheduler;
|
||||
function displayTestResult(testNumber) {
|
||||
const expectationNode = document.getElementById('test-' + testNumber + '-expected');
|
||||
@@ -215,6 +233,16 @@ const expectedResults = [
|
||||
[
|
||||
// ... TODO
|
||||
],
|
||||
[],
|
||||
[],
|
||||
// Test 8
|
||||
[
|
||||
'Queue size: 0.',
|
||||
'Pausing... press continue to resume.',
|
||||
'Queue size: 2.',
|
||||
'Finishing...',
|
||||
'Done!',
|
||||
],
|
||||
];
|
||||
function runTestOne() {
|
||||
// Test 1
|
||||
@@ -496,6 +524,51 @@ function runTestSeven() {
|
||||
}
|
||||
scheduleCallback(incrementCounterAndScheduleNextCallback);
|
||||
}
|
||||
|
||||
function runTestEight() {
|
||||
// Test 8
|
||||
// Pauses execution, dumps the queue, and continues execution
|
||||
clearTestResult(8);
|
||||
|
||||
function countNodesInStack(firstCallbackNode) {
|
||||
var node = firstCallbackNode;
|
||||
var count = 0;
|
||||
if (node !== null) {
|
||||
do {
|
||||
count = count + 1;
|
||||
node = node.next;
|
||||
} while (node !== firstCallbackNode);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
scheduleCallback(() => {
|
||||
|
||||
// size should be 0
|
||||
updateTestResult(8, `Queue size: ${countNodesInStack(getFirstCallbackNode())}.`);
|
||||
updateTestResult(8, 'Pausing... press continue to resume.');
|
||||
pauseExecution();
|
||||
|
||||
scheduleCallback(function () {
|
||||
updateTestResult(8, 'Finishing...');
|
||||
displayTestResult(8);
|
||||
})
|
||||
scheduleCallback(function () {
|
||||
updateTestResult(8, 'Done!');
|
||||
displayTestResult(8);
|
||||
checkTestResult(8);
|
||||
})
|
||||
|
||||
// new size should be 2 now
|
||||
updateTestResult(8, `Queue size: ${countNodesInStack(getFirstCallbackNode())}.`);
|
||||
displayTestResult(8);
|
||||
});
|
||||
}
|
||||
|
||||
function continueTestEight() {
|
||||
continueExecution();
|
||||
}
|
||||
|
||||
</script type="text/babel">
|
||||
</body>
|
||||
</html>
|
||||
@@ -46,7 +46,7 @@
|
||||
</p>
|
||||
<h3>
|
||||
Tests
|
||||
<button onClick="runAllTests()">Run all tests</button>
|
||||
<button id="run-test-button" onClick="runAllTests()">Run all tests</button>
|
||||
</h3>
|
||||
<ol>
|
||||
<li id="checkSchedulerAPI" data-value="...">
|
||||
@@ -62,6 +62,17 @@
|
||||
<strong>Test end-to-end integration</strong>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<script>
|
||||
if (window.location.search.includes('puppeteer=true')) {
|
||||
// Colocated calls to performance.now() often yield different values in Puppeteer.
|
||||
// This causes the Scheduler API test to fail.
|
||||
// For the purposes of our automated release scripts,
|
||||
// Coerce tests to use Date.now() instead to reduce the chances of a false positive.
|
||||
window.performance = {now: Date.now};
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Load the tracing API before react to test that it's lazily evaluated -->
|
||||
<script src="../../build/node_modules/scheduler/umd/scheduler.development.js"></script>
|
||||
<script src="../../build/node_modules/scheduler/umd/scheduler-tracing.development.js"></script>
|
||||
|
||||
@@ -20,7 +20,7 @@ Follow these steps:
|
||||
# 1: Build react from source
|
||||
cd /path/to/react
|
||||
yarn
|
||||
yarn build dom-client,core,react-cache,scheduler --type=NODE
|
||||
yarn build react-dom/index,react/index,react-cache,scheduler --type=NODE
|
||||
|
||||
# 2: Install fixture dependencies
|
||||
cd fixtures/unstable-async/suspense/
|
||||
|
||||
@@ -18,7 +18,7 @@ There are also known bugs and inefficiencies in master so **don't use this fixtu
|
||||
# 1: Build react from source
|
||||
cd /path/to/react
|
||||
yarn
|
||||
yarn build dom-client,core,react-cache,scheduler --type=NODE
|
||||
yarn build react-dom/index,react/index,react-cache,scheduler --type=NODE
|
||||
|
||||
# 2: Install fixture dependencies
|
||||
cd fixtures/unstable-async/time-slicing/
|
||||
|
||||
14
fixtures/unstable-fire/index.html
Normal file
14
fixtures/unstable-fire/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<body>
|
||||
<script src="../../build/node_modules/react/umd/react.development.js"></script>
|
||||
<script src="../../build/node_modules/react-dom/umd/react-dom-unstable-fire.development.js"></script>
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
|
||||
<div id="container"></div>
|
||||
<script type="text/babel">
|
||||
ReactFire.render(
|
||||
<h1>Hello World!</h1>,
|
||||
document.getElementById('container')
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -58,7 +58,7 @@
|
||||
"flow-bin": "^0.72.0",
|
||||
"glob": "^6.0.4",
|
||||
"glob-stream": "^6.1.0",
|
||||
"google-closure-compiler": "20180506.0.0",
|
||||
"google-closure-compiler": "20190106.0.0",
|
||||
"gzip-size": "^3.0.0",
|
||||
"jasmine-check": "^1.0.0-rc.0",
|
||||
"jest": "^23.1.0",
|
||||
@@ -84,16 +84,17 @@
|
||||
"targz": "^1.0.1",
|
||||
"through2": "^2.0.0",
|
||||
"tmp": "~0.0.28",
|
||||
"typescript": "~1.8.10"
|
||||
"typescript": "~1.8.10",
|
||||
"@mattiasbuelens/web-streams-polyfill": "0.1.0"
|
||||
},
|
||||
"devEngines": {
|
||||
"node": "8.x || 9.x || 10.x"
|
||||
"node": "8.x || 9.x || 10.x || 11.x"
|
||||
},
|
||||
"jest": {
|
||||
"testRegex": "/scripts/jest/dont-run-jest-directly\\.js$"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run version-check && node ./scripts/rollup/build.js",
|
||||
"build": "node ./scripts/rollup/build.js",
|
||||
"linc": "node ./scripts/tasks/linc.js",
|
||||
"lint": "node ./scripts/tasks/eslint.js",
|
||||
"lint-build": "node ./scripts/rollup/validate/index.js",
|
||||
|
||||
@@ -36,7 +36,7 @@ This abstraction can handle a variety of subscription types, including:
|
||||
yarn add create-subscription
|
||||
|
||||
# NPM
|
||||
npm install create-subscription --save
|
||||
npm install create-subscription
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
{
|
||||
"name": "create-subscription",
|
||||
"description": "utility for subscribing to external data sources inside React components",
|
||||
"version": "16.6.1",
|
||||
"repository": "facebook/react",
|
||||
"version": "16.8.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
"directory": "packages/create-subscription"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/"
|
||||
],
|
||||
|
||||
@@ -148,7 +148,7 @@ describe('createSubscription', () => {
|
||||
|
||||
// Test a promise that resolves before render
|
||||
// Note that this will require an extra render anyway,
|
||||
// Because there is no way to syncrhonously get a Promise's value
|
||||
// Because there is no way to synchronously get a Promise's value
|
||||
rejectB(false);
|
||||
ReactNoop.render(<Subscription source={promiseB}>{render}</Subscription>);
|
||||
expect(ReactNoop.flush()).toEqual(['loading']);
|
||||
@@ -431,7 +431,7 @@ describe('createSubscription', () => {
|
||||
'Parent.componentDidUpdate',
|
||||
]);
|
||||
|
||||
// Updates from the new subsribable should be ignored.
|
||||
// Updates from the new subscribable should be ignored.
|
||||
observableB.next('b-1');
|
||||
expect(ReactNoop.flush()).toEqual([]);
|
||||
expect(log).toEqual([
|
||||
|
||||
@@ -2,26 +2,20 @@
|
||||
|
||||
This ESLint plugin enforces the [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html).
|
||||
|
||||
It is a part of the [Hooks proposal](https://reactjs.org/docs/hooks-intro.html) for React.
|
||||
|
||||
## Experimental Status
|
||||
|
||||
This is an experimental release and is intended to be used for testing the Hooks proposal with React 16.7 alpha. The exact heuristics it uses may be adjusted.
|
||||
|
||||
The [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html) documentation contains a link to the technical RFC. Please leave a comment on the RFC if you have concerns or ideas about how this plugin should work.
|
||||
It is a part of the [Hooks API](https://reactjs.org/docs/hooks-intro.html) for React.
|
||||
|
||||
## Installation
|
||||
|
||||
**Note: If you're using Create React App, please wait for a corresponding experimental release of `react-scripts` that includes this rule 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:
|
||||
|
||||
```sh
|
||||
# npm
|
||||
npm install eslint-plugin-react-hooks@next --save-dev
|
||||
npm install eslint-plugin-react-hooks --save-dev
|
||||
|
||||
# yarn
|
||||
yarn add eslint-plugin-react-hooks@next --dev
|
||||
yarn add eslint-plugin-react-hooks --dev
|
||||
```
|
||||
|
||||
Then add it to your ESLint configuration:
|
||||
|
||||
@@ -270,6 +270,75 @@ eslintTester.run('react-hooks', ReactHooksESLintRule, {
|
||||
useState();
|
||||
}
|
||||
`,
|
||||
`
|
||||
// Valid because the loop doesn't change the order of hooks calls.
|
||||
function RegressionTest() {
|
||||
const res = [];
|
||||
const additionalCond = true;
|
||||
for (let i = 0; i !== 10 && additionalCond; ++i ) {
|
||||
res.push(i);
|
||||
}
|
||||
React.useLayoutEffect(() => {});
|
||||
}
|
||||
`,
|
||||
`
|
||||
// Is valid but hard to compute by brute-forcing
|
||||
function MyComponent() {
|
||||
// 40 conditions
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
if (c) {} else {}
|
||||
|
||||
// 10 hooks
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
useHook();
|
||||
}
|
||||
`,
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "eslint-plugin-react-hooks",
|
||||
"description": "ESLint rules for React Hooks",
|
||||
"version": "0.0.0",
|
||||
"repository": "facebook/react",
|
||||
"version": "1.0.0",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/eslint-plugin-react-hooks"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs"
|
||||
],
|
||||
|
||||
@@ -149,7 +149,14 @@ export default {
|
||||
paths += countPathsFromStart(prevSegment);
|
||||
}
|
||||
}
|
||||
cache.set(segment.id, paths);
|
||||
|
||||
// If our segment is reachable then there should be at least one path
|
||||
// to it from the start of our code path.
|
||||
if (segment.reachable && paths === 0) {
|
||||
cache.delete(segment.id);
|
||||
} else {
|
||||
cache.set(segment.id, paths);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
@@ -374,13 +381,14 @@ export default {
|
||||
for (const hook of reactHooks) {
|
||||
// Report an error if a hook may be called more then once.
|
||||
if (cycled) {
|
||||
context.report(
|
||||
hook,
|
||||
`React Hook "${context.getSource(hook)}" may be executed ` +
|
||||
context.report({
|
||||
node: hook,
|
||||
message:
|
||||
`React Hook "${context.getSource(hook)}" may be executed ` +
|
||||
'more than once. Possibly because it is called in a loop. ' +
|
||||
'React Hooks must be called in the exact same order in ' +
|
||||
'every component render.',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// If this is not a valid code path for React hooks then we need to
|
||||
@@ -394,16 +402,15 @@ export default {
|
||||
//
|
||||
// Special case when we think there might be an early return.
|
||||
if (!cycled && pathsFromStartToEnd !== allPathsFromStartToEnd) {
|
||||
context.report(
|
||||
hook,
|
||||
const message =
|
||||
`React Hook "${context.getSource(hook)}" is called ` +
|
||||
'conditionally. React Hooks must be called in the exact ' +
|
||||
'same order in every component render.' +
|
||||
(possiblyHasEarlyReturn
|
||||
? ' Did you accidentally call a React Hook after an' +
|
||||
' early return?'
|
||||
: ''),
|
||||
);
|
||||
'conditionally. React Hooks must be called in the exact ' +
|
||||
'same order in every component render.' +
|
||||
(possiblyHasEarlyReturn
|
||||
? ' Did you accidentally call a React Hook after an' +
|
||||
' early return?'
|
||||
: '');
|
||||
context.report({node: hook, message});
|
||||
}
|
||||
} else if (
|
||||
codePathNode.parent &&
|
||||
@@ -418,13 +425,12 @@ export default {
|
||||
// call in a class, if it works, is unambigously *not* a hook.
|
||||
} else if (codePathFunctionName) {
|
||||
// Custom message if we found an invalid function name.
|
||||
context.report(
|
||||
hook,
|
||||
const message =
|
||||
`React Hook "${context.getSource(hook)}" is called in ` +
|
||||
`function "${context.getSource(codePathFunctionName)}" ` +
|
||||
'which is neither a React function component or a custom ' +
|
||||
'React Hook function.',
|
||||
);
|
||||
`function "${context.getSource(codePathFunctionName)}" ` +
|
||||
'which is neither a React function component or a custom ' +
|
||||
'React Hook function.';
|
||||
context.report({node: hook, message});
|
||||
} else if (codePathNode.type === 'Program') {
|
||||
// For now, ignore if it's in top level scope.
|
||||
// We could warn here but there are false positives related
|
||||
@@ -436,12 +442,11 @@ export default {
|
||||
// enough in the common case that the incorrect message in
|
||||
// uncommon cases doesn't matter.
|
||||
if (isSomewhereInsideComponentOrHook) {
|
||||
context.report(
|
||||
hook,
|
||||
const message =
|
||||
`React Hook "${context.getSource(hook)}" cannot be called ` +
|
||||
'inside a callback. React Hooks must be called in a ' +
|
||||
'React function component or a custom React Hook function.',
|
||||
);
|
||||
'inside a callback. React Hooks must be called in a ' +
|
||||
'React function component or a custom React Hook function.';
|
||||
context.report({node: hook, message});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5
packages/jest-mock-scheduler/README.md
Normal file
5
packages/jest-mock-scheduler/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# `jest-mock-scheduler`
|
||||
|
||||
Jest matchers and utilities for testing the `scheduler` package.
|
||||
|
||||
This package is experimental. APIs may change between releases.
|
||||
8
packages/jest-mock-scheduler/index.js
Normal file
8
packages/jest-mock-scheduler/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './src/JestMockScheduler';
|
||||
7
packages/jest-mock-scheduler/npm/index.js
Normal file
7
packages/jest-mock-scheduler/npm/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/jest-mock-scheduler.production.min.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/jest-mock-scheduler.development.js');
|
||||
}
|
||||
32
packages/jest-mock-scheduler/package.json
Normal file
32
packages/jest-mock-scheduler/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "jest-mock-scheduler",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "Jest matchers and utilities for testing the scheduler package.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/jest-mock-scheduler"
|
||||
},
|
||||
"keywords": [
|
||||
"jest",
|
||||
"scheduler"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/facebook/react/issues"
|
||||
},
|
||||
"homepage": "https://reactjs.org/",
|
||||
"peerDependencies": {
|
||||
"jest": "^23.0.1",
|
||||
"scheduler": "^0.11.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/"
|
||||
]
|
||||
}
|
||||
61
packages/jest-mock-scheduler/src/JestMockScheduler.js
Normal file
61
packages/jest-mock-scheduler/src/JestMockScheduler.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
|
||||
// Math.pow(2, 30) - 1
|
||||
// 0b111111111111111111111111111111
|
||||
const maxSigned31BitInt = 1073741823;
|
||||
|
||||
export function mockRestore() {
|
||||
delete global._schedMock;
|
||||
}
|
||||
|
||||
let callback = null;
|
||||
let currentTime = -1;
|
||||
|
||||
function flushCallback(didTimeout, ms) {
|
||||
if (callback !== null) {
|
||||
let cb = callback;
|
||||
callback = null;
|
||||
try {
|
||||
currentTime = ms;
|
||||
cb(didTimeout);
|
||||
} finally {
|
||||
currentTime = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function requestHostCallback(cb, ms) {
|
||||
if (currentTime !== -1) {
|
||||
// Protect against re-entrancy.
|
||||
setTimeout(requestHostCallback, 0, cb, ms);
|
||||
} else {
|
||||
callback = cb;
|
||||
setTimeout(flushCallback, ms, true, ms);
|
||||
setTimeout(flushCallback, maxSigned31BitInt, false, maxSigned31BitInt);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelHostCallback() {
|
||||
callback = null;
|
||||
}
|
||||
|
||||
function shouldYieldToHost() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getCurrentTime() {
|
||||
return currentTime === -1 ? 0 : currentTime;
|
||||
}
|
||||
|
||||
global._schedMock = [
|
||||
requestHostCallback,
|
||||
cancelHostCallback,
|
||||
shouldYieldToHost,
|
||||
getCurrentTime,
|
||||
];
|
||||
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"name": "jest-react",
|
||||
"version": "0.4.0",
|
||||
"version": "0.6.0",
|
||||
"description": "Jest matchers and utilities for testing React components.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
"directory": "packages/jest-react"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"jest",
|
||||
@@ -22,6 +26,7 @@
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/"
|
||||
]
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"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.6.1",
|
||||
"version": "16.8.0",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-art"
|
||||
},
|
||||
"keywords": [
|
||||
"react",
|
||||
"art",
|
||||
@@ -23,7 +27,7 @@
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.11.0"
|
||||
"scheduler": "^0.13.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
@@ -31,6 +35,7 @@
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/",
|
||||
"umd/",
|
||||
|
||||
16
packages/react-art/src/ReactART.js
vendored
16
packages/react-art/src/ReactART.js
vendored
@@ -7,7 +7,11 @@
|
||||
|
||||
import React from 'react';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import * as ARTRenderer from 'react-reconciler/inline.art';
|
||||
import {
|
||||
createContainer,
|
||||
updateContainer,
|
||||
injectIntoDevTools,
|
||||
} from 'react-reconciler/inline.art';
|
||||
import Transform from 'art/core/transform';
|
||||
import Mode from 'art/modes/current';
|
||||
import FastNoSideEffects from 'art/modes/fast-noSideEffects';
|
||||
@@ -61,8 +65,8 @@ class Surface extends React.Component {
|
||||
|
||||
this._surface = Mode.Surface(+width, +height, this._tagRef);
|
||||
|
||||
this._mountNode = ARTRenderer.createContainer(this._surface);
|
||||
ARTRenderer.updateContainer(this.props.children, this._mountNode, this);
|
||||
this._mountNode = createContainer(this._surface);
|
||||
updateContainer(this.props.children, this._mountNode, this);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
@@ -72,7 +76,7 @@ class Surface extends React.Component {
|
||||
this._surface.resize(+props.width, +props.height);
|
||||
}
|
||||
|
||||
ARTRenderer.updateContainer(this.props.children, this._mountNode, this);
|
||||
updateContainer(this.props.children, this._mountNode, this);
|
||||
|
||||
if (this._surface.render) {
|
||||
this._surface.render();
|
||||
@@ -80,7 +84,7 @@ class Surface extends React.Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
ARTRenderer.updateContainer(null, this._mountNode, this);
|
||||
updateContainer(null, this._mountNode, this);
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -132,7 +136,7 @@ class Text extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
ARTRenderer.injectIntoDevTools({
|
||||
injectIntoDevTools({
|
||||
findFiberByHostInstance: () => null,
|
||||
bundleType: __DEV__ ? 1 : 0,
|
||||
version: ReactVersion,
|
||||
|
||||
6
packages/react-art/src/ReactARTHostConfig.js
vendored
6
packages/react-art/src/ReactARTHostConfig.js
vendored
@@ -5,6 +5,10 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
unstable_scheduleCallback as scheduleDeferredCallback,
|
||||
unstable_cancelCallback as cancelDeferredCallback,
|
||||
} from 'scheduler';
|
||||
export {
|
||||
unstable_now as now,
|
||||
unstable_scheduleCallback as scheduleDeferredCallback,
|
||||
@@ -337,6 +341,8 @@ export function getChildHostContext() {
|
||||
export const scheduleTimeout = setTimeout;
|
||||
export const cancelTimeout = clearTimeout;
|
||||
export const noTimeout = -1;
|
||||
export const schedulePassiveEffects = scheduleDeferredCallback;
|
||||
export const cancelPassiveEffects = cancelDeferredCallback;
|
||||
|
||||
export function shouldSetTextContent(type, props) {
|
||||
return (
|
||||
|
||||
@@ -3,10 +3,15 @@
|
||||
"name": "react-cache",
|
||||
"description": "A basic cache for React applications",
|
||||
"version": "2.0.0-alpha.0",
|
||||
"repository": "facebook/react",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-cache"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/",
|
||||
"umd/"
|
||||
|
||||
7
packages/react-cache/src/ReactCache.js
vendored
7
packages/react-cache/src/ReactCache.js
vendored
@@ -46,11 +46,12 @@ const Pending = 0;
|
||||
const Resolved = 1;
|
||||
const Rejected = 2;
|
||||
|
||||
const currentOwner =
|
||||
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner;
|
||||
const ReactCurrentDispatcher =
|
||||
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.ReactCurrentDispatcher;
|
||||
|
||||
function readContext(Context, observedBits) {
|
||||
const dispatcher = currentOwner.currentDispatcher;
|
||||
const dispatcher = ReactCurrentDispatcher.current;
|
||||
if (dispatcher === null) {
|
||||
throw new Error(
|
||||
'react-cache: read and preload may only be called from within a ' +
|
||||
|
||||
@@ -12,11 +12,16 @@
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"cjs/"
|
||||
],
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-debug-tools"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
|
||||
134
packages/react-debug-tools/src/ReactDebugHooks.js
vendored
134
packages/react-debug-tools/src/ReactDebugHooks.js
vendored
@@ -10,6 +10,7 @@
|
||||
import type {ReactContext, ReactProviderType} from 'shared/ReactTypes';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactFiber';
|
||||
import type {Hook} from 'react-reconciler/src/ReactFiberHooks';
|
||||
import type {Dispatcher as DispatcherType} from 'react-reconciler/src/ReactFiberHooks';
|
||||
|
||||
import ErrorStackParser from 'error-stack-parser';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
ForwardRef,
|
||||
} from 'shared/ReactWorkTags';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
|
||||
|
||||
// Used to track hooks called during a render
|
||||
|
||||
@@ -52,10 +53,10 @@ function getPrimitiveStackCache(): Map<string, Array<any>> {
|
||||
Dispatcher.useState(null);
|
||||
Dispatcher.useReducer((s, a) => s, null);
|
||||
Dispatcher.useRef(null);
|
||||
Dispatcher.useMutationEffect(() => {});
|
||||
Dispatcher.useLayoutEffect(() => {});
|
||||
Dispatcher.useEffect(() => {});
|
||||
Dispatcher.useImperativeMethods(undefined, () => null);
|
||||
Dispatcher.useImperativeHandle(undefined, () => null);
|
||||
Dispatcher.useDebugValue(null);
|
||||
Dispatcher.useCallback(() => {});
|
||||
Dispatcher.useMemo(() => null);
|
||||
} finally {
|
||||
@@ -114,13 +115,18 @@ function useState<S>(
|
||||
return [state, (action: BasicStateAction<S>) => {}];
|
||||
}
|
||||
|
||||
function useReducer<S, A>(
|
||||
function useReducer<S, I, A>(
|
||||
reducer: (S, A) => S,
|
||||
initialState: S,
|
||||
initialAction: A | void | null,
|
||||
initialArg: I,
|
||||
init?: I => S,
|
||||
): [S, Dispatch<A>] {
|
||||
let hook = nextHook();
|
||||
let state = hook !== null ? hook.memoizedState : initialState;
|
||||
let state;
|
||||
if (hook !== null) {
|
||||
state = hook.memoizedState;
|
||||
} else {
|
||||
state = init !== undefined ? init(initialArg) : ((initialArg: any): S);
|
||||
}
|
||||
hookLog.push({
|
||||
primitive: 'Reducer',
|
||||
stackError: new Error(),
|
||||
@@ -140,20 +146,8 @@ function useRef<T>(initialValue: T): {current: T} {
|
||||
return ref;
|
||||
}
|
||||
|
||||
function useMutationEffect(
|
||||
create: () => mixed,
|
||||
inputs: Array<mixed> | void | null,
|
||||
): void {
|
||||
nextHook();
|
||||
hookLog.push({
|
||||
primitive: 'MutationEffect',
|
||||
stackError: new Error(),
|
||||
value: create,
|
||||
});
|
||||
}
|
||||
|
||||
function useLayoutEffect(
|
||||
create: () => mixed,
|
||||
create: () => (() => void) | void,
|
||||
inputs: Array<mixed> | void | null,
|
||||
): void {
|
||||
nextHook();
|
||||
@@ -165,14 +159,14 @@ function useLayoutEffect(
|
||||
}
|
||||
|
||||
function useEffect(
|
||||
create: () => mixed,
|
||||
create: () => (() => void) | void,
|
||||
inputs: Array<mixed> | void | null,
|
||||
): void {
|
||||
nextHook();
|
||||
hookLog.push({primitive: 'Effect', stackError: new Error(), value: create});
|
||||
}
|
||||
|
||||
function useImperativeMethods<T>(
|
||||
function useImperativeHandle<T>(
|
||||
ref: {current: T | null} | ((inst: T | null) => mixed) | null | void,
|
||||
create: () => T,
|
||||
inputs: Array<mixed> | void | null,
|
||||
@@ -187,12 +181,20 @@ function useImperativeMethods<T>(
|
||||
instance = ref.current;
|
||||
}
|
||||
hookLog.push({
|
||||
primitive: 'ImperativeMethods',
|
||||
primitive: 'ImperativeHandle',
|
||||
stackError: new Error(),
|
||||
value: instance,
|
||||
});
|
||||
}
|
||||
|
||||
function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
|
||||
hookLog.push({
|
||||
primitive: 'DebugValue',
|
||||
stackError: new Error(),
|
||||
value: typeof formatterFn === 'function' ? formatterFn(value) : value,
|
||||
});
|
||||
}
|
||||
|
||||
function useCallback<T>(callback: T, inputs: Array<mixed> | void | null): T {
|
||||
let hook = nextHook();
|
||||
hookLog.push({
|
||||
@@ -213,15 +215,15 @@ function useMemo<T>(
|
||||
return value;
|
||||
}
|
||||
|
||||
const Dispatcher = {
|
||||
const Dispatcher: DispatcherType = {
|
||||
readContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeMethods,
|
||||
useImperativeHandle,
|
||||
useDebugValue,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useMutationEffect,
|
||||
useReducer,
|
||||
useRef,
|
||||
useState,
|
||||
@@ -402,7 +404,7 @@ function buildTree(rootStack, readHookLog): HooksTree {
|
||||
let children = [];
|
||||
levelChildren.push({
|
||||
name: parseCustomHookName(stack[j - 1].functionName),
|
||||
value: undefined, // TODO: Support custom inspectable values.
|
||||
value: undefined,
|
||||
subHooks: children,
|
||||
});
|
||||
stackOfChildren.push(levelChildren);
|
||||
@@ -416,16 +418,61 @@ function buildTree(rootStack, readHookLog): HooksTree {
|
||||
subHooks: [],
|
||||
});
|
||||
}
|
||||
|
||||
// Associate custom hook values (useDebugValue() hook entries) with the correct hooks.
|
||||
processDebugValues(rootChildren, null);
|
||||
|
||||
return rootChildren;
|
||||
}
|
||||
|
||||
// Custom hooks support user-configurable labels (via the special useDebugValue() hook).
|
||||
// That hook adds user-provided values to the hooks tree,
|
||||
// but these values aren't intended to appear alongside of the other hooks.
|
||||
// Instead they should be attributed to their parent custom hook.
|
||||
// This method walks the tree and assigns debug values to their custom hook owners.
|
||||
function processDebugValues(
|
||||
hooksTree: HooksTree,
|
||||
parentHooksNode: HooksNode | null,
|
||||
): void {
|
||||
let debugValueHooksNodes: Array<HooksNode> = [];
|
||||
|
||||
for (let i = 0; i < hooksTree.length; i++) {
|
||||
const hooksNode = hooksTree[i];
|
||||
if (hooksNode.name === 'DebugValue' && hooksNode.subHooks.length === 0) {
|
||||
hooksTree.splice(i, 1);
|
||||
i--;
|
||||
debugValueHooksNodes.push(hooksNode);
|
||||
} else {
|
||||
processDebugValues(hooksNode.subHooks, hooksNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble debug value labels to their custom hook owner.
|
||||
// If there is no parent hook, just ignore them for now.
|
||||
// (We may warn about this in the future.)
|
||||
if (parentHooksNode !== null) {
|
||||
if (debugValueHooksNodes.length === 1) {
|
||||
parentHooksNode.value = debugValueHooksNodes[0].value;
|
||||
} else if (debugValueHooksNodes.length > 1) {
|
||||
parentHooksNode.value = debugValueHooksNodes.map(({value}) => value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function inspectHooks<Props>(
|
||||
renderFunction: Props => React$Node,
|
||||
props: Props,
|
||||
currentDispatcher: ?CurrentDispatcherRef,
|
||||
): HooksTree {
|
||||
let previousDispatcher = ReactCurrentOwner.currentDispatcher;
|
||||
// DevTools will pass the current renderer's injected dispatcher.
|
||||
// Other apps might compile debug hooks as part of their app though.
|
||||
if (currentDispatcher == null) {
|
||||
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
||||
}
|
||||
|
||||
let previousDispatcher = currentDispatcher.current;
|
||||
let readHookLog;
|
||||
ReactCurrentOwner.currentDispatcher = Dispatcher;
|
||||
currentDispatcher.current = Dispatcher;
|
||||
let ancestorStackError;
|
||||
try {
|
||||
ancestorStackError = new Error();
|
||||
@@ -433,7 +480,7 @@ export function inspectHooks<Props>(
|
||||
} finally {
|
||||
readHookLog = hookLog;
|
||||
hookLog = [];
|
||||
ReactCurrentOwner.currentDispatcher = previousDispatcher;
|
||||
currentDispatcher.current = previousDispatcher;
|
||||
}
|
||||
let rootStack = ErrorStackParser.parse(ancestorStackError);
|
||||
return buildTree(rootStack, readHookLog);
|
||||
@@ -464,10 +511,11 @@ function inspectHooksOfForwardRef<Props, Ref>(
|
||||
renderFunction: (Props, Ref) => React$Node,
|
||||
props: Props,
|
||||
ref: Ref,
|
||||
currentDispatcher: CurrentDispatcherRef,
|
||||
): HooksTree {
|
||||
let previousDispatcher = ReactCurrentOwner.currentDispatcher;
|
||||
let previousDispatcher = currentDispatcher.current;
|
||||
let readHookLog;
|
||||
ReactCurrentOwner.currentDispatcher = Dispatcher;
|
||||
currentDispatcher.current = Dispatcher;
|
||||
let ancestorStackError;
|
||||
try {
|
||||
ancestorStackError = new Error();
|
||||
@@ -475,7 +523,7 @@ function inspectHooksOfForwardRef<Props, Ref>(
|
||||
} finally {
|
||||
readHookLog = hookLog;
|
||||
hookLog = [];
|
||||
ReactCurrentOwner.currentDispatcher = previousDispatcher;
|
||||
currentDispatcher.current = previousDispatcher;
|
||||
}
|
||||
let rootStack = ErrorStackParser.parse(ancestorStackError);
|
||||
return buildTree(rootStack, readHookLog);
|
||||
@@ -496,7 +544,16 @@ function resolveDefaultProps(Component, baseProps) {
|
||||
return baseProps;
|
||||
}
|
||||
|
||||
export function inspectHooksOfFiber(fiber: Fiber) {
|
||||
export function inspectHooksOfFiber(
|
||||
fiber: Fiber,
|
||||
currentDispatcher: ?CurrentDispatcherRef,
|
||||
) {
|
||||
// DevTools will pass the current renderer's injected dispatcher.
|
||||
// Other apps might compile debug hooks as part of their app though.
|
||||
if (currentDispatcher == null) {
|
||||
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
||||
}
|
||||
|
||||
if (
|
||||
fiber.tag !== FunctionComponent &&
|
||||
fiber.tag !== SimpleMemoComponent &&
|
||||
@@ -520,9 +577,14 @@ export function inspectHooksOfFiber(fiber: Fiber) {
|
||||
try {
|
||||
setupContexts(contextMap, fiber);
|
||||
if (fiber.tag === ForwardRef) {
|
||||
return inspectHooksOfForwardRef(type.render, props, fiber.ref);
|
||||
return inspectHooksOfForwardRef(
|
||||
type.render,
|
||||
props,
|
||||
fiber.ref,
|
||||
currentDispatcher,
|
||||
);
|
||||
}
|
||||
return inspectHooks(type, props);
|
||||
return inspectHooks(type, props, currentDispatcher);
|
||||
} finally {
|
||||
currentHook = null;
|
||||
restoreContexts(contextMap);
|
||||
|
||||
@@ -16,9 +16,6 @@ let ReactDebugTools;
|
||||
describe('ReactHooksInspection', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
let ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
// TODO: Switch this test to non-internal once the flag is on by default.
|
||||
ReactFeatureFlags.enableHooks = true;
|
||||
React = require('react');
|
||||
ReactDebugTools = require('react-debug-tools');
|
||||
});
|
||||
@@ -41,6 +38,7 @@ describe('ReactHooksInspection', () => {
|
||||
it('should inspect a simple custom hook', () => {
|
||||
function useCustom(value) {
|
||||
let [state] = React.useState(value);
|
||||
React.useDebugValue('custom hook label');
|
||||
return state;
|
||||
}
|
||||
function Foo(props) {
|
||||
@@ -51,7 +49,7 @@ describe('ReactHooksInspection', () => {
|
||||
expect(tree).toEqual([
|
||||
{
|
||||
name: 'Custom',
|
||||
value: undefined,
|
||||
value: __DEV__ ? 'custom hook label' : undefined,
|
||||
subHooks: [
|
||||
{
|
||||
name: 'State',
|
||||
@@ -129,7 +127,7 @@ describe('ReactHooksInspection', () => {
|
||||
return result;
|
||||
}
|
||||
function useBaz(value) {
|
||||
React.useMutationEffect(effect);
|
||||
React.useLayoutEffect(effect);
|
||||
let result = useCustom(value);
|
||||
return result;
|
||||
}
|
||||
@@ -176,7 +174,7 @@ describe('ReactHooksInspection', () => {
|
||||
value: undefined,
|
||||
subHooks: [
|
||||
{
|
||||
name: 'MutationEffect',
|
||||
name: 'LayoutEffect',
|
||||
value: effect,
|
||||
subHooks: [],
|
||||
},
|
||||
@@ -216,4 +214,67 @@ describe('ReactHooksInspection', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support an injected dispatcher', () => {
|
||||
function Foo(props) {
|
||||
let [state] = React.useState('hello world');
|
||||
return <div>{state}</div>;
|
||||
}
|
||||
|
||||
let initial = {};
|
||||
let current = initial;
|
||||
let getterCalls = 0;
|
||||
let setterCalls = [];
|
||||
let FakeDispatcherRef = {
|
||||
get current() {
|
||||
getterCalls++;
|
||||
return current;
|
||||
},
|
||||
set current(value) {
|
||||
setterCalls.push(value);
|
||||
current = value;
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => {
|
||||
ReactDebugTools.inspectHooks(Foo, {}, FakeDispatcherRef);
|
||||
}).toThrow(
|
||||
'Hooks can only be called inside the body of a function component.',
|
||||
);
|
||||
|
||||
expect(getterCalls).toBe(1);
|
||||
expect(setterCalls).toHaveLength(2);
|
||||
expect(setterCalls[0]).not.toBe(initial);
|
||||
expect(setterCalls[1]).toBe(initial);
|
||||
});
|
||||
|
||||
describe('useDebugValue', () => {
|
||||
it('should be ignored when called outside of a custom hook', () => {
|
||||
function Foo(props) {
|
||||
React.useDebugValue('this is invalid');
|
||||
return null;
|
||||
}
|
||||
let tree = ReactDebugTools.inspectHooks(Foo, {});
|
||||
expect(tree).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should support an optional formatter function param', () => {
|
||||
function useCustom() {
|
||||
React.useDebugValue({bar: 123}, object => `bar:${object.bar}`);
|
||||
React.useState(0);
|
||||
}
|
||||
function Foo(props) {
|
||||
useCustom();
|
||||
return null;
|
||||
}
|
||||
let tree = ReactDebugTools.inspectHooks(Foo, {});
|
||||
expect(tree).toEqual([
|
||||
{
|
||||
name: 'Custom',
|
||||
value: __DEV__ ? 'bar:123' : undefined,
|
||||
subHooks: [{name: 'State', subHooks: [], value: 0}],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -13,15 +13,14 @@
|
||||
let React;
|
||||
let ReactTestRenderer;
|
||||
let ReactDebugTools;
|
||||
let act;
|
||||
|
||||
describe('ReactHooksInspectionIntergration', () => {
|
||||
describe('ReactHooksInspectionIntegration', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
let ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
// TODO: Switch this test to non-internal once the flag is on by default.
|
||||
ReactFeatureFlags.enableHooks = true;
|
||||
React = require('react');
|
||||
ReactTestRenderer = require('react-test-renderer');
|
||||
act = ReactTestRenderer.act;
|
||||
ReactDebugTools = require('react-debug-tools');
|
||||
});
|
||||
|
||||
@@ -50,7 +49,7 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
onMouseUp: setStateB,
|
||||
} = renderer.root.findByType('div').props;
|
||||
|
||||
setStateA('Hi');
|
||||
act(() => setStateA('Hi'));
|
||||
|
||||
childFiber = renderer.root.findByType(Foo)._currentFiber();
|
||||
tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
@@ -60,7 +59,7 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
{name: 'State', value: 'world', subHooks: []},
|
||||
]);
|
||||
|
||||
setStateB('world!');
|
||||
act(() => setStateB('world!'));
|
||||
|
||||
childFiber = renderer.root.findByType(Foo)._currentFiber();
|
||||
tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
@@ -79,11 +78,10 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
let [state2, dispatch] = React.useReducer((s, a) => a.value, 'b');
|
||||
let ref = React.useRef('c');
|
||||
|
||||
React.useMutationEffect(effect);
|
||||
React.useLayoutEffect(effect);
|
||||
React.useEffect(effect);
|
||||
|
||||
React.useImperativeMethods(
|
||||
React.useImperativeHandle(
|
||||
outsideRef,
|
||||
() => {
|
||||
// Return a function so that jest treats them as non-equal.
|
||||
@@ -95,8 +93,12 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
React.useMemo(() => state1 + state2, [state1]);
|
||||
|
||||
function update() {
|
||||
setState('A');
|
||||
dispatch({value: 'B'});
|
||||
act(() => {
|
||||
setState('A');
|
||||
});
|
||||
act(() => {
|
||||
dispatch({value: 'B'});
|
||||
});
|
||||
ref.current = 'C';
|
||||
}
|
||||
let memoizedUpdate = React.useCallback(update, []);
|
||||
@@ -117,10 +119,9 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
{name: 'State', value: 'a', subHooks: []},
|
||||
{name: 'Reducer', value: 'b', subHooks: []},
|
||||
{name: 'Ref', value: 'c', subHooks: []},
|
||||
{name: 'MutationEffect', value: effect, subHooks: []},
|
||||
{name: 'LayoutEffect', value: effect, subHooks: []},
|
||||
{name: 'Effect', value: effect, subHooks: []},
|
||||
{name: 'ImperativeMethods', value: outsideRef.current, subHooks: []},
|
||||
{name: 'ImperativeHandle', value: outsideRef.current, subHooks: []},
|
||||
{name: 'Memo', value: 'ab', subHooks: []},
|
||||
{name: 'Callback', value: updateStates, subHooks: []},
|
||||
]);
|
||||
@@ -134,10 +135,9 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
{name: 'State', value: 'A', subHooks: []},
|
||||
{name: 'Reducer', value: 'B', subHooks: []},
|
||||
{name: 'Ref', value: 'C', subHooks: []},
|
||||
{name: 'MutationEffect', value: effect, subHooks: []},
|
||||
{name: 'LayoutEffect', value: effect, subHooks: []},
|
||||
{name: 'Effect', value: effect, subHooks: []},
|
||||
{name: 'ImperativeMethods', value: outsideRef.current, subHooks: []},
|
||||
{name: 'ImperativeHandle', value: outsideRef.current, subHooks: []},
|
||||
{name: 'Memo', value: 'Ab', subHooks: []},
|
||||
{name: 'Callback', value: updateStates, subHooks: []},
|
||||
]);
|
||||
@@ -168,7 +168,7 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
it('should inspect forwardRef', () => {
|
||||
let obj = function() {};
|
||||
let Foo = React.forwardRef(function(props, ref) {
|
||||
React.useImperativeMethods(ref, () => obj);
|
||||
React.useImperativeHandle(ref, () => obj);
|
||||
return <div />;
|
||||
});
|
||||
let ref = React.createRef();
|
||||
@@ -177,7 +177,7 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
let childFiber = renderer.root.findByType(Foo)._currentFiber();
|
||||
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toEqual([
|
||||
{name: 'ImperativeMethods', value: obj, subHooks: []},
|
||||
{name: 'ImperativeHandle', value: obj, subHooks: []},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -215,6 +215,154 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
describe('useDebugValue', () => {
|
||||
it('should support inspectable values for multiple custom hooks', () => {
|
||||
function useLabeledValue(label) {
|
||||
let [value] = React.useState(label);
|
||||
React.useDebugValue(`custom label ${label}`);
|
||||
return value;
|
||||
}
|
||||
function useAnonymous(label) {
|
||||
let [value] = React.useState(label);
|
||||
return value;
|
||||
}
|
||||
function Example() {
|
||||
useLabeledValue('a');
|
||||
React.useState('b');
|
||||
useAnonymous('c');
|
||||
useLabeledValue('d');
|
||||
return null;
|
||||
}
|
||||
let renderer = ReactTestRenderer.create(<Example />);
|
||||
let childFiber = renderer.root.findByType(Example)._currentFiber();
|
||||
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toEqual([
|
||||
{
|
||||
name: 'LabeledValue',
|
||||
value: __DEV__ ? 'custom label a' : undefined,
|
||||
subHooks: [{name: 'State', value: 'a', subHooks: []}],
|
||||
},
|
||||
{
|
||||
name: 'State',
|
||||
value: 'b',
|
||||
subHooks: [],
|
||||
},
|
||||
{
|
||||
name: 'Anonymous',
|
||||
value: undefined,
|
||||
subHooks: [{name: 'State', value: 'c', subHooks: []}],
|
||||
},
|
||||
{
|
||||
name: 'LabeledValue',
|
||||
value: __DEV__ ? 'custom label d' : undefined,
|
||||
subHooks: [{name: 'State', value: 'd', subHooks: []}],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support inspectable values for nested custom hooks', () => {
|
||||
function useInner() {
|
||||
React.useDebugValue('inner');
|
||||
React.useState(0);
|
||||
}
|
||||
function useOuter() {
|
||||
React.useDebugValue('outer');
|
||||
useInner();
|
||||
}
|
||||
function Example() {
|
||||
useOuter();
|
||||
return null;
|
||||
}
|
||||
let renderer = ReactTestRenderer.create(<Example />);
|
||||
let childFiber = renderer.root.findByType(Example)._currentFiber();
|
||||
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toEqual([
|
||||
{
|
||||
name: 'Outer',
|
||||
value: __DEV__ ? 'outer' : undefined,
|
||||
subHooks: [
|
||||
{
|
||||
name: 'Inner',
|
||||
value: __DEV__ ? 'inner' : undefined,
|
||||
subHooks: [{name: 'State', value: 0, subHooks: []}],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support multiple inspectable values per custom hooks', () => {
|
||||
function useMultiLabelCustom() {
|
||||
React.useDebugValue('one');
|
||||
React.useDebugValue('two');
|
||||
React.useDebugValue('three');
|
||||
React.useState(0);
|
||||
}
|
||||
function useSingleLabelCustom(value) {
|
||||
React.useDebugValue(`single ${value}`);
|
||||
React.useState(0);
|
||||
}
|
||||
function Example() {
|
||||
useSingleLabelCustom('one');
|
||||
useMultiLabelCustom();
|
||||
useSingleLabelCustom('two');
|
||||
return null;
|
||||
}
|
||||
let renderer = ReactTestRenderer.create(<Example />);
|
||||
let childFiber = renderer.root.findByType(Example)._currentFiber();
|
||||
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toEqual([
|
||||
{
|
||||
name: 'SingleLabelCustom',
|
||||
value: __DEV__ ? 'single one' : undefined,
|
||||
subHooks: [{name: 'State', value: 0, subHooks: []}],
|
||||
},
|
||||
{
|
||||
name: 'MultiLabelCustom',
|
||||
value: __DEV__ ? ['one', 'two', 'three'] : undefined,
|
||||
subHooks: [{name: 'State', value: 0, subHooks: []}],
|
||||
},
|
||||
{
|
||||
name: 'SingleLabelCustom',
|
||||
value: __DEV__ ? 'single two' : undefined,
|
||||
subHooks: [{name: 'State', value: 0, subHooks: []}],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should ignore useDebugValue() made outside of a custom hook', () => {
|
||||
function Example() {
|
||||
React.useDebugValue('this is invalid');
|
||||
return null;
|
||||
}
|
||||
let renderer = ReactTestRenderer.create(<Example />);
|
||||
let childFiber = renderer.root.findByType(Example)._currentFiber();
|
||||
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should support an optional formatter function param', () => {
|
||||
function useCustom() {
|
||||
React.useDebugValue({bar: 123}, object => `bar:${object.bar}`);
|
||||
React.useState(0);
|
||||
}
|
||||
function Example() {
|
||||
useCustom();
|
||||
return null;
|
||||
}
|
||||
let renderer = ReactTestRenderer.create(<Example />);
|
||||
let childFiber = renderer.root.findByType(Example)._currentFiber();
|
||||
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toEqual([
|
||||
{
|
||||
name: 'Custom',
|
||||
value: __DEV__ ? 'bar:123' : undefined,
|
||||
subHooks: [{name: 'State', subHooks: [], value: 0}],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support defaultProps and lazy', async () => {
|
||||
let Suspense = React.Suspense;
|
||||
|
||||
@@ -244,4 +392,39 @@ describe('ReactHooksInspectionIntergration', () => {
|
||||
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
expect(tree).toEqual([{name: 'State', value: 'def', subHooks: []}]);
|
||||
});
|
||||
|
||||
it('should support an injected dispatcher', () => {
|
||||
function Foo(props) {
|
||||
let [state] = React.useState('hello world');
|
||||
return <div>{state}</div>;
|
||||
}
|
||||
|
||||
let initial = {};
|
||||
let current = initial;
|
||||
let getterCalls = 0;
|
||||
let setterCalls = [];
|
||||
let FakeDispatcherRef = {
|
||||
get current() {
|
||||
getterCalls++;
|
||||
return current;
|
||||
},
|
||||
set current(value) {
|
||||
setterCalls.push(value);
|
||||
current = value;
|
||||
},
|
||||
};
|
||||
|
||||
let renderer = ReactTestRenderer.create(<Foo />);
|
||||
let childFiber = renderer.root._currentFiber();
|
||||
expect(() => {
|
||||
ReactDebugTools.inspectHooksOfFiber(childFiber, FakeDispatcherRef);
|
||||
}).toThrow(
|
||||
'Hooks can only be called inside the body of a function component.',
|
||||
);
|
||||
|
||||
expect(getterCalls).toBe(1);
|
||||
expect(setterCalls).toHaveLength(2);
|
||||
expect(setterCalls[0]).not.toBe(initial);
|
||||
expect(setterCalls[1]).toBe(initial);
|
||||
});
|
||||
});
|
||||
7
packages/react-dom/npm/unstable-fizz.browser.js
Normal file
7
packages/react-dom/npm/unstable-fizz.browser.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-dom-unstable-fizz.browser.production.min.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-dom-unstable-fizz.browser.development.js');
|
||||
}
|
||||
3
packages/react-dom/npm/unstable-fizz.js
vendored
Normal file
3
packages/react-dom/npm/unstable-fizz.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./unstable-fizz.node');
|
||||
7
packages/react-dom/npm/unstable-fizz.node.js
Normal file
7
packages/react-dom/npm/unstable-fizz.node.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
module.exports = require('./cjs/react-dom-unstable-fizz.node.production.min.js');
|
||||
} else {
|
||||
module.exports = require('./cjs/react-dom-unstable-fizz.node.development.js');
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "16.6.1",
|
||||
"version": "16.8.0",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
"directory": "packages/react-dom"
|
||||
},
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
@@ -16,7 +20,7 @@
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.11.0"
|
||||
"scheduler": "^0.13.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.0.0"
|
||||
@@ -24,18 +28,24 @@
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"build-info.json",
|
||||
"index.js",
|
||||
"profiling.js",
|
||||
"server.js",
|
||||
"server.browser.js",
|
||||
"server.node.js",
|
||||
"test-utils.js",
|
||||
"unstable-fire.js",
|
||||
"unstable-fizz.js",
|
||||
"unstable-fizz.browser.js",
|
||||
"unstable-fizz.node.js",
|
||||
"unstable-native-dependencies.js",
|
||||
"cjs/",
|
||||
"umd/"
|
||||
],
|
||||
"browser": {
|
||||
"./server.js": "./server.browser.js"
|
||||
"./server.js": "./server.browser.js",
|
||||
"./unstable-fizz.js": "./unstable-fizz.browser.js"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
|
||||
@@ -1201,6 +1201,40 @@ describe('ReactComponentLifeCycle', () => {
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
|
||||
it('should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps', () => {
|
||||
const divRef = React.createRef();
|
||||
class SimpleComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: props.value,
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.value === prevState.value) {
|
||||
return null;
|
||||
}
|
||||
return {value: nextProps.value};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextState.value !== this.state.value;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div ref={divRef}>value: {this.state.value}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
|
||||
ReactDOM.render(<SimpleComponent value="initial" />, div);
|
||||
expect(divRef.current.textContent).toBe('value: initial');
|
||||
ReactDOM.render(<SimpleComponent value="updated" />, div);
|
||||
expect(divRef.current.textContent).toBe('value: updated');
|
||||
});
|
||||
|
||||
it('should call getSnapshotBeforeUpdate before mutations are committed', () => {
|
||||
const log = [];
|
||||
|
||||
|
||||
@@ -1743,6 +1743,25 @@ describe('ReactCompositeComponent', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn about reassigning this.props while rendering', () => {
|
||||
class Bad extends React.Component {
|
||||
componentDidMount() {}
|
||||
componentDidUpdate() {}
|
||||
render() {
|
||||
this.props = {...this.props};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
expect(() => {
|
||||
ReactDOM.render(<Bad />, container);
|
||||
}).toWarnDev(
|
||||
'It looks like Bad is reassigning its own `this.props` while rendering. ' +
|
||||
'This is not supported and can lead to confusing bugs.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return error if render is not defined', () => {
|
||||
class RenderTestUndefinedRender extends React.Component {}
|
||||
|
||||
|
||||
@@ -450,22 +450,6 @@ describe('ReactDOM', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('warns when requestAnimationFrame is not polyfilled', () => {
|
||||
const previousRAF = global.requestAnimationFrame;
|
||||
try {
|
||||
delete global.requestAnimationFrame;
|
||||
jest.resetModules();
|
||||
spyOnDevAndProd(console, 'error');
|
||||
require('react-dom');
|
||||
expect(console.error.calls.count()).toEqual(1);
|
||||
expect(console.error.calls.argsFor(0)[0]).toMatch(
|
||||
"This browser doesn't support requestAnimationFrame.",
|
||||
);
|
||||
} finally {
|
||||
global.requestAnimationFrame = previousRAF;
|
||||
}
|
||||
});
|
||||
|
||||
it('reports stacks with re-entrant renderToString() calls on the client', () => {
|
||||
function Child2(props) {
|
||||
return <span ariaTypo3="no">{props.children}</span>;
|
||||
|
||||
@@ -491,5 +491,191 @@ describe('ReactDOMFiberAsync', () => {
|
||||
expect(container.textContent).toEqual('1');
|
||||
expect(returnValue).toBe(undefined);
|
||||
});
|
||||
|
||||
it('ignores discrete events on a pending removed element', () => {
|
||||
const disableButtonRef = React.createRef();
|
||||
const submitButtonRef = React.createRef();
|
||||
|
||||
let formSubmitted = false;
|
||||
|
||||
class Form extends React.Component {
|
||||
state = {active: true};
|
||||
disableForm = () => {
|
||||
this.setState({active: false});
|
||||
};
|
||||
submitForm = () => {
|
||||
formSubmitted = true; // This should not get invoked
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.disableForm} ref={disableButtonRef}>
|
||||
Disable
|
||||
</button>
|
||||
{this.state.active ? (
|
||||
<button onClick={this.submitForm} ref={submitButtonRef}>
|
||||
Submit
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(<Form />);
|
||||
// Flush
|
||||
jest.runAllTimers();
|
||||
|
||||
let disableButton = disableButtonRef.current;
|
||||
expect(disableButton.tagName).toBe('BUTTON');
|
||||
|
||||
// Dispatch a click event on the Disable-button.
|
||||
let firstEvent = document.createEvent('Event');
|
||||
firstEvent.initEvent('click', true, true);
|
||||
disableButton.dispatchEvent(firstEvent);
|
||||
|
||||
// There should now be a pending update to disable the form.
|
||||
|
||||
// This should not have flushed yet since it's in concurrent mode.
|
||||
let submitButton = submitButtonRef.current;
|
||||
expect(submitButton.tagName).toBe('BUTTON');
|
||||
|
||||
// In the meantime, we can dispatch a new client event on the submit button.
|
||||
let secondEvent = document.createEvent('Event');
|
||||
secondEvent.initEvent('click', true, true);
|
||||
// This should force the pending update to flush which disables the submit button before the event is invoked.
|
||||
submitButton.dispatchEvent(secondEvent);
|
||||
|
||||
// Therefore the form should never have been submitted.
|
||||
expect(formSubmitted).toBe(false);
|
||||
|
||||
expect(submitButtonRef.current).toBe(null);
|
||||
});
|
||||
|
||||
it('ignores discrete events on a pending removed event listener', () => {
|
||||
const disableButtonRef = React.createRef();
|
||||
const submitButtonRef = React.createRef();
|
||||
|
||||
let formSubmitted = false;
|
||||
|
||||
class Form extends React.Component {
|
||||
state = {active: true};
|
||||
disableForm = () => {
|
||||
this.setState({active: false});
|
||||
};
|
||||
submitForm = () => {
|
||||
formSubmitted = true; // This should not get invoked
|
||||
};
|
||||
disabledSubmitForm = () => {
|
||||
// The form is disabled.
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.disableForm} ref={disableButtonRef}>
|
||||
Disable
|
||||
</button>
|
||||
<button
|
||||
onClick={
|
||||
this.state.active ? this.submitForm : this.disabledSubmitForm
|
||||
}
|
||||
ref={submitButtonRef}>
|
||||
Submit
|
||||
</button>{' '}
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(<Form />);
|
||||
// Flush
|
||||
jest.runAllTimers();
|
||||
|
||||
let disableButton = disableButtonRef.current;
|
||||
expect(disableButton.tagName).toBe('BUTTON');
|
||||
|
||||
// Dispatch a click event on the Disable-button.
|
||||
let firstEvent = document.createEvent('Event');
|
||||
firstEvent.initEvent('click', true, true);
|
||||
disableButton.dispatchEvent(firstEvent);
|
||||
|
||||
// There should now be a pending update to disable the form.
|
||||
|
||||
// This should not have flushed yet since it's in concurrent mode.
|
||||
let submitButton = submitButtonRef.current;
|
||||
expect(submitButton.tagName).toBe('BUTTON');
|
||||
|
||||
// In the meantime, we can dispatch a new client event on the submit button.
|
||||
let secondEvent = document.createEvent('Event');
|
||||
secondEvent.initEvent('click', true, true);
|
||||
// This should force the pending update to flush which disables the submit button before the event is invoked.
|
||||
submitButton.dispatchEvent(secondEvent);
|
||||
|
||||
// Therefore the form should never have been submitted.
|
||||
expect(formSubmitted).toBe(false);
|
||||
});
|
||||
|
||||
it('uses the newest discrete events on a pending changed event listener', () => {
|
||||
const enableButtonRef = React.createRef();
|
||||
const submitButtonRef = React.createRef();
|
||||
|
||||
let formSubmitted = false;
|
||||
|
||||
class Form extends React.Component {
|
||||
state = {active: false};
|
||||
enableForm = () => {
|
||||
this.setState({active: true});
|
||||
};
|
||||
submitForm = () => {
|
||||
formSubmitted = true; // This should happen
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.enableForm} ref={enableButtonRef}>
|
||||
Enable
|
||||
</button>
|
||||
<button
|
||||
onClick={this.state.active ? this.submitForm : null}
|
||||
ref={submitButtonRef}>
|
||||
Submit
|
||||
</button>{' '}
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(<Form />);
|
||||
// Flush
|
||||
jest.runAllTimers();
|
||||
|
||||
let enableButton = enableButtonRef.current;
|
||||
expect(enableButton.tagName).toBe('BUTTON');
|
||||
|
||||
// Dispatch a click event on the Enable-button.
|
||||
let firstEvent = document.createEvent('Event');
|
||||
firstEvent.initEvent('click', true, true);
|
||||
enableButton.dispatchEvent(firstEvent);
|
||||
|
||||
// There should now be a pending update to enable the form.
|
||||
|
||||
// This should not have flushed yet since it's in concurrent mode.
|
||||
let submitButton = submitButtonRef.current;
|
||||
expect(submitButton.tagName).toBe('BUTTON');
|
||||
|
||||
// In the meantime, we can dispatch a new client event on the submit button.
|
||||
let secondEvent = document.createEvent('Event');
|
||||
secondEvent.initEvent('click', true, true);
|
||||
// This should force the pending update to flush which enables the submit button before the event is invoked.
|
||||
submitButton.dispatchEvent(secondEvent);
|
||||
|
||||
// Therefore the form should have been submitted.
|
||||
expect(formSubmitted).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
45
packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js
vendored
Normal file
45
packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
// Polyfills for test environment
|
||||
global.ReadableStream = require('@mattiasbuelens/web-streams-polyfill/ponyfill/es6').ReadableStream;
|
||||
global.TextEncoder = require('util').TextEncoder;
|
||||
|
||||
let React;
|
||||
let ReactDOMFizzServer;
|
||||
|
||||
describe('ReactDOMFizzServer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOMFizzServer = require('react-dom/unstable-fizz.browser');
|
||||
});
|
||||
|
||||
async function readResult(stream) {
|
||||
let reader = stream.getReader();
|
||||
let result = '';
|
||||
while (true) {
|
||||
let {done, value} = await reader.read();
|
||||
if (done) {
|
||||
return result;
|
||||
}
|
||||
result += Buffer.from(value).toString('utf8');
|
||||
}
|
||||
}
|
||||
|
||||
it('should call renderToReadableStream', async () => {
|
||||
let stream = ReactDOMFizzServer.renderToReadableStream(
|
||||
<div>hello world</div>,
|
||||
);
|
||||
let result = await readResult(stream);
|
||||
expect(result).toBe('<div>hello world</div>');
|
||||
});
|
||||
});
|
||||
39
packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
vendored
Normal file
39
packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let Stream;
|
||||
let React;
|
||||
let ReactDOMFizzServer;
|
||||
|
||||
describe('ReactDOMFizzServer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOMFizzServer = require('react-dom/unstable-fizz');
|
||||
Stream = require('stream');
|
||||
});
|
||||
|
||||
function getTestWritable() {
|
||||
let writable = new Stream.PassThrough();
|
||||
writable.setEncoding('utf8');
|
||||
writable.result = '';
|
||||
writable.on('data', chunk => (writable.result += chunk));
|
||||
return writable;
|
||||
}
|
||||
|
||||
it('should call pipeToNodeWritable', () => {
|
||||
let writable = getTestWritable();
|
||||
ReactDOMFizzServer.pipeToNodeWritable(<div>hello world</div>, writable);
|
||||
jest.runAllTimers();
|
||||
expect(writable.result).toBe('<div>hello world</div>');
|
||||
});
|
||||
});
|
||||
103
packages/react-dom/src/__tests__/ReactDOMHooks-test.js
vendored
Normal file
103
packages/react-dom/src/__tests__/ReactDOMHooks-test.js
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* 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';
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
|
||||
describe('ReactDOMSuspensePlaceholder', () => {
|
||||
let container;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
it('should not bail out when an update is scheduled from within an event handler', () => {
|
||||
const {createRef, useCallback, useState} = React;
|
||||
|
||||
const Example = ({inputRef, labelRef}) => {
|
||||
const [text, setText] = useState('');
|
||||
const handleInput = useCallback(event => {
|
||||
setText(event.target.value);
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<input ref={inputRef} onInput={handleInput} />
|
||||
<label ref={labelRef}>{text}</label>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const inputRef = createRef();
|
||||
const labelRef = createRef();
|
||||
|
||||
ReactDOM.render(
|
||||
<Example inputRef={inputRef} labelRef={labelRef} />,
|
||||
container,
|
||||
);
|
||||
|
||||
inputRef.current.value = 'abc';
|
||||
inputRef.current.dispatchEvent(
|
||||
new Event('input', {bubbles: true, cancelable: true}),
|
||||
);
|
||||
|
||||
expect(labelRef.current.innerHTML).toBe('abc');
|
||||
});
|
||||
|
||||
it('should not bail out when an update is scheduled from within an event handler in ConcurrentMode', () => {
|
||||
const {createRef, useCallback, useState} = React;
|
||||
|
||||
const Example = ({inputRef, labelRef}) => {
|
||||
const [text, setText] = useState('');
|
||||
const handleInput = useCallback(event => {
|
||||
setText(event.target.value);
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<input ref={inputRef} onInput={handleInput} />
|
||||
<label ref={labelRef}>{text}</label>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const inputRef = createRef();
|
||||
const labelRef = createRef();
|
||||
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(
|
||||
<React.unstable_ConcurrentMode>
|
||||
<Example inputRef={inputRef} labelRef={labelRef} />
|
||||
</React.unstable_ConcurrentMode>,
|
||||
);
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
inputRef.current.value = 'abc';
|
||||
inputRef.current.dispatchEvent(
|
||||
new Event('input', {bubbles: true, cancelable: true}),
|
||||
);
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(labelRef.current.innerHTML).toBe('abc');
|
||||
});
|
||||
});
|
||||
@@ -374,4 +374,104 @@ describe('ReactDOMRoot', () => {
|
||||
'unstable_createRoot(...): Target container is not a DOM element.',
|
||||
);
|
||||
});
|
||||
|
||||
it('warns when rendering with legacy API into createRoot() container', () => {
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(<div>Hi</div>);
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('Hi');
|
||||
expect(() => {
|
||||
ReactDOM.render(<div>Bye</div>, container);
|
||||
}).toWarnDev(
|
||||
[
|
||||
// We care about this warning:
|
||||
'You are calling ReactDOM.render() on a container that was previously ' +
|
||||
'passed to ReactDOM.unstable_createRoot(). This is not supported. ' +
|
||||
'Did you mean to call root.render(element)?',
|
||||
// This is more of a symptom but restructuring the code to avoid it isn't worth it:
|
||||
'Replacing React-rendered children with a new root component.',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
jest.runAllTimers();
|
||||
// This works now but we could disallow it:
|
||||
expect(container.textContent).toEqual('Bye');
|
||||
});
|
||||
|
||||
it('warns when hydrating with legacy API into createRoot() container', () => {
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(<div>Hi</div>);
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('Hi');
|
||||
expect(() => {
|
||||
ReactDOM.hydrate(<div>Hi</div>, container);
|
||||
}).toWarnDev(
|
||||
[
|
||||
// We care about this warning:
|
||||
'You are calling ReactDOM.hydrate() on a container that was previously ' +
|
||||
'passed to ReactDOM.unstable_createRoot(). This is not supported. ' +
|
||||
'Did you mean to call root.render(element, {hydrate: true})?',
|
||||
// This is more of a symptom but restructuring the code to avoid it isn't worth it:
|
||||
'Replacing React-rendered children with a new root component.',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
|
||||
it('warns when unmounting with legacy API (no previous content)', () => {
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(<div>Hi</div>);
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('Hi');
|
||||
let unmounted = false;
|
||||
expect(() => {
|
||||
unmounted = ReactDOM.unmountComponentAtNode(container);
|
||||
}).toWarnDev(
|
||||
[
|
||||
// We care about this warning:
|
||||
'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' +
|
||||
'passed to ReactDOM.unstable_createRoot(). This is not supported. Did you mean to call root.unmount()?',
|
||||
// This is more of a symptom but restructuring the code to avoid it isn't worth it:
|
||||
"The node you're attempting to unmount was rendered by React and is not a top-level container.",
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(unmounted).toBe(false);
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('Hi');
|
||||
root.unmount();
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('');
|
||||
});
|
||||
|
||||
it('warns when unmounting with legacy API (has previous content)', () => {
|
||||
// Currently createRoot().render() doesn't clear this.
|
||||
container.appendChild(document.createElement('div'));
|
||||
// The rest is the same as test above.
|
||||
const root = ReactDOM.unstable_createRoot(container);
|
||||
root.render(<div>Hi</div>);
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('Hi');
|
||||
let unmounted = false;
|
||||
expect(() => {
|
||||
unmounted = ReactDOM.unmountComponentAtNode(container);
|
||||
}).toWarnDev('Did you mean to call root.unmount()?', {withoutStack: true});
|
||||
expect(unmounted).toBe(false);
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('Hi');
|
||||
root.unmount();
|
||||
jest.runAllTimers();
|
||||
expect(container.textContent).toEqual('');
|
||||
});
|
||||
|
||||
it('warns when passing legacy container to createRoot()', () => {
|
||||
ReactDOM.render(<div>Hi</div>, container);
|
||||
expect(() => {
|
||||
ReactDOM.unstable_createRoot(container);
|
||||
}).toWarnDev(
|
||||
'You are calling ReactDOM.unstable_createRoot() on a container that was previously ' +
|
||||
'passed to ReactDOM.render(). This is not supported.',
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,9 +24,9 @@ let useContext;
|
||||
let useCallback;
|
||||
let useMemo;
|
||||
let useRef;
|
||||
let useImperativeMethods;
|
||||
let useMutationEffect;
|
||||
let useImperativeHandle;
|
||||
let useLayoutEffect;
|
||||
let useDebugValue;
|
||||
let forwardRef;
|
||||
let yieldedValues;
|
||||
let yieldValue;
|
||||
@@ -38,7 +38,6 @@ function initModules() {
|
||||
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
|
||||
ReactFeatureFlags.enableHooks = true;
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
@@ -49,8 +48,8 @@ function initModules() {
|
||||
useCallback = React.useCallback;
|
||||
useMemo = React.useMemo;
|
||||
useRef = React.useRef;
|
||||
useImperativeMethods = React.useImperativeMethods;
|
||||
useMutationEffect = React.useMutationEffect;
|
||||
useDebugValue = React.useDebugValue;
|
||||
useImperativeHandle = React.useImperativeHandle;
|
||||
useLayoutEffect = React.useLayoutEffect;
|
||||
forwardRef = React.forwardRef;
|
||||
|
||||
@@ -209,12 +208,12 @@ describe('ReactDOMServerHooks', () => {
|
||||
expect(domNode.textContent).toEqual('0');
|
||||
});
|
||||
|
||||
itRenders('lazy initialization with initialAction', async render => {
|
||||
itRenders('lazy initialization', async render => {
|
||||
function reducer(state, action) {
|
||||
return action === 'increment' ? state + 1 : state;
|
||||
}
|
||||
function Counter() {
|
||||
let [count] = useReducer(reducer, 0, 'increment');
|
||||
let [count] = useReducer(reducer, 0, c => c + 1);
|
||||
yieldValue('Render: ' + count);
|
||||
return <Text text={count} />;
|
||||
}
|
||||
@@ -419,6 +418,52 @@ describe('ReactDOMServerHooks', () => {
|
||||
expect(domNode.textContent).toEqual('HELLO, WORLD.');
|
||||
},
|
||||
);
|
||||
|
||||
itRenders('with a warning for useState inside useMemo', async render => {
|
||||
function App() {
|
||||
useMemo(() => {
|
||||
useState();
|
||||
return 0;
|
||||
});
|
||||
return 'hi';
|
||||
}
|
||||
|
||||
const domNode = await render(<App />, 1);
|
||||
expect(domNode.textContent).toEqual('hi');
|
||||
});
|
||||
|
||||
itThrowsWhenRendering(
|
||||
'with a warning for useRef inside useReducer',
|
||||
async render => {
|
||||
function App() {
|
||||
const [value, dispatch] = useReducer((state, action) => {
|
||||
useRef(0);
|
||||
return state + 1;
|
||||
}, 0);
|
||||
if (value === 0) {
|
||||
dispatch();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const domNode = await render(<App />, 1);
|
||||
expect(domNode.textContent).toEqual('1');
|
||||
},
|
||||
'Rendered more hooks than during the previous render',
|
||||
);
|
||||
|
||||
itRenders('with a warning for useRef inside useState', async render => {
|
||||
function App() {
|
||||
const [value] = useState(() => {
|
||||
useRef(0);
|
||||
return 0;
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
const domNode = await render(<App />, 1);
|
||||
expect(domNode.textContent).toEqual('0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useRef', () => {
|
||||
@@ -516,12 +561,25 @@ describe('ReactDOMServerHooks', () => {
|
||||
expect(domNode.tagName).toEqual('SPAN');
|
||||
expect(domNode.textContent).toEqual('Count: 0');
|
||||
});
|
||||
|
||||
itRenders('should support render time callbacks', async render => {
|
||||
function Counter(props) {
|
||||
const renderCount = useCallback(increment => {
|
||||
return 'Count: ' + (props.count + increment);
|
||||
});
|
||||
return <Text text={renderCount(3)} />;
|
||||
}
|
||||
const domNode = await render(<Counter count={2} />);
|
||||
expect(clearYields()).toEqual(['Count: 5']);
|
||||
expect(domNode.tagName).toEqual('SPAN');
|
||||
expect(domNode.textContent).toEqual('Count: 5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useImperativeMethods', () => {
|
||||
describe('useImperativeHandle', () => {
|
||||
it('should not be invoked on the server', async () => {
|
||||
function Counter(props, ref) {
|
||||
useImperativeMethods(ref, () => {
|
||||
useImperativeHandle(ref, () => {
|
||||
throw new Error('should not be invoked');
|
||||
});
|
||||
return <Text text={props.label + ': ' + ref.current} />;
|
||||
@@ -538,22 +596,6 @@ describe('ReactDOMServerHooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('useMutationEffect', () => {
|
||||
it('should warn when invoked during render', async () => {
|
||||
function Counter() {
|
||||
useMutationEffect(() => {
|
||||
throw new Error('should not be invoked');
|
||||
});
|
||||
|
||||
return <Text text="Count: 0" />;
|
||||
}
|
||||
const domNode = await serverRender(<Counter />, 1);
|
||||
expect(clearYields()).toEqual(['Count: 0']);
|
||||
expect(domNode.tagName).toEqual('SPAN');
|
||||
expect(domNode.textContent).toEqual('Count: 0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useLayoutEffect', () => {
|
||||
it('should warn when invoked during render', async () => {
|
||||
function Counter() {
|
||||
@@ -571,8 +613,114 @@ describe('ReactDOMServerHooks', () => {
|
||||
});
|
||||
|
||||
describe('useContext', () => {
|
||||
itThrowsWhenRendering(
|
||||
'if used inside a class component',
|
||||
async render => {
|
||||
const Context = React.createContext({}, () => {});
|
||||
class Counter extends React.Component {
|
||||
render() {
|
||||
let [count] = useContext(Context);
|
||||
return <Text text={count} />;
|
||||
}
|
||||
}
|
||||
|
||||
return render(<Counter />);
|
||||
},
|
||||
'Hooks can only be called inside the body of a function component.',
|
||||
);
|
||||
});
|
||||
|
||||
itRenders(
|
||||
'can use the same context multiple times in the same function',
|
||||
async render => {
|
||||
const Context = React.createContext({foo: 0, bar: 0, baz: 0});
|
||||
|
||||
function Provider(props) {
|
||||
return (
|
||||
<Context.Provider
|
||||
value={{foo: props.foo, bar: props.bar, baz: props.baz}}>
|
||||
{props.children}
|
||||
</Context.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function FooAndBar() {
|
||||
const {foo} = useContext(Context);
|
||||
const {bar} = useContext(Context);
|
||||
return <Text text={`Foo: ${foo}, Bar: ${bar}`} />;
|
||||
}
|
||||
|
||||
function Baz() {
|
||||
const {baz} = useContext(Context);
|
||||
return <Text text={'Baz: ' + baz} />;
|
||||
}
|
||||
|
||||
class Indirection extends React.Component {
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
function App(props) {
|
||||
return (
|
||||
<div>
|
||||
<Provider foo={props.foo} bar={props.bar} baz={props.baz}>
|
||||
<Indirection>
|
||||
<Indirection>
|
||||
<FooAndBar />
|
||||
</Indirection>
|
||||
<Indirection>
|
||||
<Baz />
|
||||
</Indirection>
|
||||
</Indirection>
|
||||
</Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const domNode = await render(<App foo={1} bar={3} baz={5} />);
|
||||
expect(clearYields()).toEqual(['Foo: 1, Bar: 3', 'Baz: 5']);
|
||||
expect(domNode.childNodes.length).toBe(2);
|
||||
expect(domNode.firstChild.tagName).toEqual('SPAN');
|
||||
expect(domNode.firstChild.textContent).toEqual('Foo: 1, Bar: 3');
|
||||
expect(domNode.lastChild.tagName).toEqual('SPAN');
|
||||
expect(domNode.lastChild.textContent).toEqual('Baz: 5');
|
||||
},
|
||||
);
|
||||
|
||||
itRenders('warns when bitmask is passed to useContext', async render => {
|
||||
let Context = React.createContext('Hi');
|
||||
|
||||
function Foo() {
|
||||
return <span>{useContext(Context, 1)}</span>;
|
||||
}
|
||||
|
||||
const domNode = await render(<Foo />, 1);
|
||||
expect(domNode.textContent).toBe('Hi');
|
||||
});
|
||||
|
||||
describe('useDebugValue', () => {
|
||||
itRenders('is a noop', async render => {
|
||||
function Counter(props) {
|
||||
const debugValue = useDebugValue(123);
|
||||
return <Text text={typeof debugValue} />;
|
||||
}
|
||||
|
||||
const domNode = await render(<Counter />);
|
||||
expect(domNode.textContent).toEqual('undefined');
|
||||
});
|
||||
});
|
||||
|
||||
describe('readContext', () => {
|
||||
function readContext(Context, observedBits) {
|
||||
const dispatcher =
|
||||
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.ReactCurrentDispatcher.current;
|
||||
return dispatcher.readContext(Context, observedBits);
|
||||
}
|
||||
|
||||
itRenders(
|
||||
'can use the same context multiple times in the same function',
|
||||
'can read the same context multiple times in the same function',
|
||||
async render => {
|
||||
const Context = React.createContext(
|
||||
{foo: 0, bar: 0, baz: 0},
|
||||
@@ -601,13 +749,13 @@ describe('ReactDOMServerHooks', () => {
|
||||
}
|
||||
|
||||
function FooAndBar() {
|
||||
const {foo} = useContext(Context, 0b001);
|
||||
const {bar} = useContext(Context, 0b010);
|
||||
const {foo} = readContext(Context, 0b001);
|
||||
const {bar} = readContext(Context, 0b010);
|
||||
return <Text text={`Foo: ${foo}, Bar: ${bar}`} />;
|
||||
}
|
||||
|
||||
function Baz() {
|
||||
const {baz} = useContext(Context, 0b100);
|
||||
const {baz} = readContext(Context, 0b100);
|
||||
return <Text text={'Baz: ' + baz} />;
|
||||
}
|
||||
|
||||
@@ -647,20 +795,27 @@ describe('ReactDOMServerHooks', () => {
|
||||
},
|
||||
);
|
||||
|
||||
itThrowsWhenRendering(
|
||||
'if used inside a class component',
|
||||
async render => {
|
||||
const Context = React.createContext({}, () => {});
|
||||
class Counter extends React.Component {
|
||||
render() {
|
||||
let [count] = useContext(Context);
|
||||
return <Text text={count} />;
|
||||
}
|
||||
}
|
||||
itRenders('with a warning inside useMemo and useReducer', async render => {
|
||||
const Context = React.createContext(42);
|
||||
|
||||
return render(<Counter />);
|
||||
},
|
||||
'Hooks can only be called inside the body of a function component.',
|
||||
);
|
||||
function ReadInMemo(props) {
|
||||
let count = React.useMemo(() => readContext(Context), []);
|
||||
return <Text text={count} />;
|
||||
}
|
||||
|
||||
function ReadInReducer(props) {
|
||||
let [count, dispatch] = React.useReducer(() => readContext(Context));
|
||||
if (count !== 42) {
|
||||
dispatch();
|
||||
}
|
||||
return <Text text={count} />;
|
||||
}
|
||||
|
||||
const domNode1 = await render(<ReadInMemo />, 1);
|
||||
expect(domNode1.textContent).toEqual('42');
|
||||
|
||||
const domNode2 = await render(<ReadInReducer />, 1);
|
||||
expect(domNode2.textContent).toEqual('42');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,9 +37,9 @@ describe('ReactDOMServerIntegration', () => {
|
||||
});
|
||||
|
||||
describe('context', function() {
|
||||
let PurpleContext, RedContext, Consumer;
|
||||
let Context, PurpleContextProvider, RedContextProvider, Consumer;
|
||||
beforeEach(() => {
|
||||
let Context = React.createContext('none');
|
||||
Context = React.createContext('none');
|
||||
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
@@ -51,8 +51,12 @@ describe('ReactDOMServerIntegration', () => {
|
||||
}
|
||||
}
|
||||
Consumer = Context.Consumer;
|
||||
PurpleContext = props => <Parent text="purple">{props.children}</Parent>;
|
||||
RedContext = props => <Parent text="red">{props.children}</Parent>;
|
||||
PurpleContextProvider = props => (
|
||||
<Parent text="purple">{props.children}</Parent>
|
||||
);
|
||||
RedContextProvider = props => (
|
||||
<Parent text="red">{props.children}</Parent>
|
||||
);
|
||||
});
|
||||
|
||||
itRenders('class child with context', async render => {
|
||||
@@ -67,9 +71,9 @@ describe('ReactDOMServerIntegration', () => {
|
||||
}
|
||||
|
||||
const e = await render(
|
||||
<PurpleContext>
|
||||
<PurpleContextProvider>
|
||||
<ClassChildWithContext />
|
||||
</PurpleContext>,
|
||||
</PurpleContextProvider>,
|
||||
);
|
||||
expect(e.textContent).toBe('purple');
|
||||
});
|
||||
@@ -80,9 +84,9 @@ describe('ReactDOMServerIntegration', () => {
|
||||
}
|
||||
|
||||
const e = await render(
|
||||
<PurpleContext>
|
||||
<PurpleContextProvider>
|
||||
<FunctionChildWithContext />
|
||||
</PurpleContext>,
|
||||
</PurpleContextProvider>,
|
||||
);
|
||||
expect(e.textContent).toBe('purple');
|
||||
});
|
||||
@@ -127,9 +131,9 @@ describe('ReactDOMServerIntegration', () => {
|
||||
const Child = props => <Grandchild />;
|
||||
|
||||
const e = await render(
|
||||
<PurpleContext>
|
||||
<PurpleContextProvider>
|
||||
<Child />
|
||||
</PurpleContext>,
|
||||
</PurpleContextProvider>,
|
||||
);
|
||||
expect(e.textContent).toBe('purple');
|
||||
});
|
||||
@@ -144,15 +148,54 @@ describe('ReactDOMServerIntegration', () => {
|
||||
};
|
||||
|
||||
const e = await render(
|
||||
<PurpleContext>
|
||||
<RedContext>
|
||||
<PurpleContextProvider>
|
||||
<RedContextProvider>
|
||||
<Grandchild />
|
||||
</RedContext>
|
||||
</PurpleContext>,
|
||||
</RedContextProvider>
|
||||
</PurpleContextProvider>,
|
||||
);
|
||||
expect(e.textContent).toBe('red');
|
||||
});
|
||||
|
||||
itRenders('readContext() in different components', async render => {
|
||||
function readContext(Ctx, observedBits) {
|
||||
const dispatcher =
|
||||
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.ReactCurrentDispatcher.current;
|
||||
return dispatcher.readContext(Ctx, observedBits);
|
||||
}
|
||||
|
||||
class Cls extends React.Component {
|
||||
render() {
|
||||
return readContext(Context);
|
||||
}
|
||||
}
|
||||
function Fn() {
|
||||
return readContext(Context);
|
||||
}
|
||||
const Memo = React.memo(() => {
|
||||
return readContext(Context);
|
||||
});
|
||||
const FwdRef = React.forwardRef((props, ref) => {
|
||||
return readContext(Context);
|
||||
});
|
||||
|
||||
const e = await render(
|
||||
<PurpleContextProvider>
|
||||
<RedContextProvider>
|
||||
<span>
|
||||
<Fn />
|
||||
<Cls />
|
||||
<Memo />
|
||||
<FwdRef />
|
||||
<Consumer>{() => readContext(Context)}</Consumer>
|
||||
</span>
|
||||
</RedContextProvider>
|
||||
</PurpleContextProvider>,
|
||||
);
|
||||
expect(e.textContent).toBe('redredredredred');
|
||||
});
|
||||
|
||||
itRenders('multiple contexts', async render => {
|
||||
const Theme = React.createContext('dark');
|
||||
const Language = React.createContext('french');
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
describe('ReactDOMShorthandCSSPropertyCollision', () => {
|
||||
let ReactFeatureFlags;
|
||||
let React;
|
||||
let ReactDOM;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutShorthandPropertyCollision = true;
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
});
|
||||
|
||||
it('should warn for conflicting CSS shorthand updates', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(<div style={{font: 'foo', fontStyle: 'bar'}} />, container);
|
||||
expect(() =>
|
||||
ReactDOM.render(<div style={{font: 'foo'}} />, container),
|
||||
).toWarnDev(
|
||||
'Warning: Removing a style property during rerender (fontStyle) ' +
|
||||
'when a conflicting property is set (font) can lead to styling ' +
|
||||
"bugs. To avoid this, don't mix shorthand and non-shorthand " +
|
||||
'properties for the same value; instead, replace the shorthand ' +
|
||||
'with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
|
||||
// These updates are OK and don't warn:
|
||||
ReactDOM.render(<div style={{font: 'qux', fontStyle: 'bar'}} />, container);
|
||||
ReactDOM.render(<div style={{font: 'foo', fontStyle: 'baz'}} />, container);
|
||||
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<div style={{font: 'qux', fontStyle: 'baz'}} />,
|
||||
container,
|
||||
),
|
||||
).toWarnDev(
|
||||
'Warning: Updating a style property during rerender (font) when ' +
|
||||
'a conflicting property is set (fontStyle) can lead to styling ' +
|
||||
"bugs. To avoid this, don't mix shorthand and non-shorthand " +
|
||||
'properties for the same value; instead, replace the shorthand ' +
|
||||
'with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
expect(() =>
|
||||
ReactDOM.render(<div style={{fontStyle: 'baz'}} />, container),
|
||||
).toWarnDev(
|
||||
'Warning: Removing a style property during rerender (font) when ' +
|
||||
'a conflicting property is set (fontStyle) can lead to styling ' +
|
||||
"bugs. To avoid this, don't mix shorthand and non-shorthand " +
|
||||
'properties for the same value; instead, replace the shorthand ' +
|
||||
'with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
|
||||
// A bit of a special case: backgroundPosition isn't technically longhand
|
||||
// (it expands to backgroundPosition{X,Y} but so does background)
|
||||
ReactDOM.render(
|
||||
<div style={{background: 'yellow', backgroundPosition: 'center'}} />,
|
||||
container,
|
||||
);
|
||||
expect(() =>
|
||||
ReactDOM.render(<div style={{background: 'yellow'}} />, container),
|
||||
).toWarnDev(
|
||||
'Warning: Removing a style property during rerender ' +
|
||||
'(backgroundPosition) when a conflicting property is set ' +
|
||||
"(background) can lead to styling bugs. To avoid this, don't mix " +
|
||||
'shorthand and non-shorthand properties for the same value; ' +
|
||||
'instead, replace the shorthand with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
ReactDOM.render(
|
||||
<div style={{background: 'yellow', backgroundPosition: 'center'}} />,
|
||||
container,
|
||||
);
|
||||
// But setting them at the same time is OK:
|
||||
ReactDOM.render(
|
||||
<div style={{background: 'green', backgroundPosition: 'top'}} />,
|
||||
container,
|
||||
);
|
||||
expect(() =>
|
||||
ReactDOM.render(<div style={{backgroundPosition: 'top'}} />, container),
|
||||
).toWarnDev(
|
||||
'Warning: Removing a style property during rerender (background) ' +
|
||||
'when a conflicting property is set (backgroundPosition) can lead ' +
|
||||
"to styling bugs. To avoid this, don't mix shorthand and " +
|
||||
'non-shorthand properties for the same value; instead, replace the ' +
|
||||
'shorthand with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
|
||||
// A bit of an even more special case: borderLeft and borderStyle overlap.
|
||||
ReactDOM.render(
|
||||
<div style={{borderStyle: 'dotted', borderLeft: '1px solid red'}} />,
|
||||
container,
|
||||
);
|
||||
expect(() =>
|
||||
ReactDOM.render(<div style={{borderLeft: '1px solid red'}} />, container),
|
||||
).toWarnDev(
|
||||
'Warning: Removing a style property during rerender (borderStyle) ' +
|
||||
'when a conflicting property is set (borderLeft) can lead to ' +
|
||||
"styling bugs. To avoid this, don't mix shorthand and " +
|
||||
'non-shorthand properties for the same value; instead, replace the ' +
|
||||
'shorthand with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
expect(() =>
|
||||
ReactDOM.render(
|
||||
<div style={{borderStyle: 'dashed', borderLeft: '1px solid red'}} />,
|
||||
container,
|
||||
),
|
||||
).toWarnDev(
|
||||
'Warning: Updating a style property during rerender (borderStyle) ' +
|
||||
'when a conflicting property is set (borderLeft) can lead to ' +
|
||||
"styling bugs. To avoid this, don't mix shorthand and " +
|
||||
'non-shorthand properties for the same value; instead, replace the ' +
|
||||
'shorthand with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
// But setting them at the same time is OK:
|
||||
ReactDOM.render(
|
||||
<div style={{borderStyle: 'dotted', borderLeft: '2px solid red'}} />,
|
||||
container,
|
||||
);
|
||||
expect(() =>
|
||||
ReactDOM.render(<div style={{borderStyle: 'dotted'}} />, container),
|
||||
).toWarnDev(
|
||||
'Warning: Removing a style property during rerender (borderLeft) ' +
|
||||
'when a conflicting property is set (borderStyle) can lead to ' +
|
||||
"styling bugs. To avoid this, don't mix shorthand and " +
|
||||
'non-shorthand properties for the same value; instead, replace the ' +
|
||||
'shorthand with separate values.' +
|
||||
'\n in div (at **)',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -9,25 +9,27 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let ReactFeatureFlags;
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let Suspense;
|
||||
let ReactCache;
|
||||
let ReactTestUtils;
|
||||
let TextResource;
|
||||
let act;
|
||||
|
||||
describe('ReactDOMSuspensePlaceholder', () => {
|
||||
let container;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.enableHooks = true;
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactCache = require('react-cache');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
act = ReactTestUtils.act;
|
||||
Suspense = React.Suspense;
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
TextResource = ReactCache.unstable_createResource(([text, ms = 0]) => {
|
||||
return new Promise((resolve, reject) =>
|
||||
@@ -38,6 +40,10 @@ describe('ReactDOMSuspensePlaceholder', () => {
|
||||
}, ([text, ms]) => text);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
function advanceTimers(ms) {
|
||||
// Note: This advances Jest's virtual time but not React's. Use
|
||||
// ReactNoop.expire for that.
|
||||
@@ -140,12 +146,14 @@ describe('ReactDOMSuspensePlaceholder', () => {
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, container);
|
||||
act(() => {
|
||||
ReactDOM.render(<App />, container);
|
||||
});
|
||||
expect(container.innerHTML).toEqual(
|
||||
'<span style="display: none;">Sibling</span><span style="display: none;"></span>Loading...',
|
||||
);
|
||||
|
||||
setIsVisible(true);
|
||||
act(() => setIsVisible(true));
|
||||
expect(container.innerHTML).toEqual(
|
||||
'<span style="display: none;">Sibling</span><span style="display: none;"></span>Loading...',
|
||||
);
|
||||
@@ -157,4 +165,64 @@ describe('ReactDOMSuspensePlaceholder', () => {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Regression test for https://github.com/facebook/react/issues/14188
|
||||
it('can call findDOMNode() in a suspended component commit phase', async () => {
|
||||
const log = [];
|
||||
const Lazy = React.lazy(
|
||||
() =>
|
||||
new Promise(resolve =>
|
||||
resolve({
|
||||
default() {
|
||||
return 'lazy';
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
class Child extends React.Component {
|
||||
componentDidMount() {
|
||||
log.push('cDM ' + this.props.id);
|
||||
ReactDOM.findDOMNode(this);
|
||||
}
|
||||
componentDidUpdate() {
|
||||
log.push('cDU ' + this.props.id);
|
||||
ReactDOM.findDOMNode(this);
|
||||
}
|
||||
render() {
|
||||
return 'child';
|
||||
}
|
||||
}
|
||||
|
||||
const buttonRef = React.createRef();
|
||||
class App extends React.Component {
|
||||
state = {
|
||||
suspend: false,
|
||||
};
|
||||
handleClick = () => {
|
||||
this.setState({suspend: true});
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<React.Suspense fallback="Loading">
|
||||
<Child id="first" />
|
||||
<button ref={buttonRef} onClick={this.handleClick}>
|
||||
Suspend
|
||||
</button>
|
||||
<Child id="second" />
|
||||
{this.state.suspend && <Lazy />}
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, container);
|
||||
|
||||
expect(log).toEqual(['cDM first', 'cDM second']);
|
||||
log.length = 0;
|
||||
|
||||
buttonRef.current.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
||||
await Lazy;
|
||||
expect(log).toEqual(['cDU first', 'cDU second']);
|
||||
});
|
||||
});
|
||||
@@ -28,6 +28,8 @@ describe('ReactErrorBoundaries', () => {
|
||||
let BrokenComponentWillMountErrorBoundary;
|
||||
let BrokenComponentDidMountErrorBoundary;
|
||||
let BrokenRender;
|
||||
let BrokenUseEffect;
|
||||
let BrokenUseLayoutEffect;
|
||||
let ErrorBoundary;
|
||||
let ErrorMessage;
|
||||
let NoopErrorBoundary;
|
||||
@@ -35,6 +37,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
let Normal;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.resetModules();
|
||||
PropTypes = require('prop-types');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
@@ -386,6 +389,28 @@ describe('ReactErrorBoundaries', () => {
|
||||
}
|
||||
};
|
||||
|
||||
BrokenUseEffect = props => {
|
||||
log.push('BrokenUseEffect render');
|
||||
|
||||
React.useEffect(() => {
|
||||
log.push('BrokenUseEffect useEffect [!]');
|
||||
throw new Error('Hello');
|
||||
});
|
||||
|
||||
return props.children;
|
||||
};
|
||||
|
||||
BrokenUseLayoutEffect = props => {
|
||||
log.push('BrokenUseLayoutEffect render');
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
log.push('BrokenUseLayoutEffect useLayoutEffect [!]');
|
||||
throw new Error('Hello');
|
||||
});
|
||||
|
||||
return props.children;
|
||||
};
|
||||
|
||||
NoopErrorBoundary = class extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -1795,6 +1820,67 @@ describe('ReactErrorBoundaries', () => {
|
||||
expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
|
||||
});
|
||||
|
||||
it('catches errors in useEffect', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<ErrorBoundary>
|
||||
<BrokenUseEffect>Initial value</BrokenUseEffect>
|
||||
</ErrorBoundary>,
|
||||
container,
|
||||
);
|
||||
expect(log).toEqual([
|
||||
'ErrorBoundary constructor',
|
||||
'ErrorBoundary componentWillMount',
|
||||
'ErrorBoundary render success',
|
||||
'BrokenUseEffect render',
|
||||
'ErrorBoundary componentDidMount',
|
||||
]);
|
||||
|
||||
expect(container.firstChild.textContent).toBe('Initial value');
|
||||
log.length = 0;
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
// Flush passive effects and handle the error
|
||||
expect(log).toEqual([
|
||||
'BrokenUseEffect useEffect [!]',
|
||||
// Handle the error
|
||||
'ErrorBoundary static getDerivedStateFromError',
|
||||
'ErrorBoundary componentWillUpdate',
|
||||
'ErrorBoundary render error',
|
||||
'ErrorBoundary componentDidUpdate',
|
||||
]);
|
||||
|
||||
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
|
||||
});
|
||||
|
||||
it('catches errors in useLayoutEffect', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<ErrorBoundary>
|
||||
<BrokenUseLayoutEffect>Initial value</BrokenUseLayoutEffect>
|
||||
</ErrorBoundary>,
|
||||
container,
|
||||
);
|
||||
expect(log).toEqual([
|
||||
'ErrorBoundary constructor',
|
||||
'ErrorBoundary componentWillMount',
|
||||
'ErrorBoundary render success',
|
||||
'BrokenUseLayoutEffect render',
|
||||
'BrokenUseLayoutEffect useLayoutEffect [!]',
|
||||
// Fiber proceeds with the hooks
|
||||
'ErrorBoundary componentDidMount',
|
||||
// The error propagates to the higher boundary
|
||||
'ErrorBoundary static getDerivedStateFromError',
|
||||
// Fiber retries from the root
|
||||
'ErrorBoundary componentWillUpdate',
|
||||
'ErrorBoundary render error',
|
||||
'ErrorBoundary componentDidUpdate',
|
||||
]);
|
||||
|
||||
expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
|
||||
});
|
||||
|
||||
it('propagates errors inside boundary during componentDidMount', () => {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
|
||||
@@ -192,7 +192,9 @@ describe('ReactFunctionComponent', () => {
|
||||
ReactTestUtils.renderIntoDocument(<ParentUsingStringRef />),
|
||||
).toWarnDev(
|
||||
'Warning: Function components cannot be given refs. ' +
|
||||
'Attempts to access this ref will fail.\n\nCheck the render method ' +
|
||||
'Attempts to access this ref will fail. ' +
|
||||
'Did you mean to use React.forwardRef()?\n\n' +
|
||||
'Check the render method ' +
|
||||
'of `ParentUsingStringRef`.\n' +
|
||||
' in FunctionComponent (at **)\n' +
|
||||
' in div (at **)\n' +
|
||||
@@ -228,7 +230,9 @@ describe('ReactFunctionComponent', () => {
|
||||
ReactTestUtils.renderIntoDocument(<ParentUsingFunctionRef />),
|
||||
).toWarnDev(
|
||||
'Warning: Function components cannot be given refs. ' +
|
||||
'Attempts to access this ref will fail.\n\nCheck the render method ' +
|
||||
'Attempts to access this ref will fail. ' +
|
||||
'Did you mean to use React.forwardRef()?\n\n' +
|
||||
'Check the render method ' +
|
||||
'of `ParentUsingFunctionRef`.\n' +
|
||||
' in FunctionComponent (at **)\n' +
|
||||
' in div (at **)\n' +
|
||||
@@ -332,7 +336,9 @@ describe('ReactFunctionComponent', () => {
|
||||
|
||||
expect(() => ReactTestUtils.renderIntoDocument(<Parent />)).toWarnDev(
|
||||
'Warning: Function components cannot be given refs. ' +
|
||||
'Attempts to access this ref will fail.\n\nCheck the render method ' +
|
||||
'Attempts to access this ref will fail. ' +
|
||||
'Did you mean to use React.forwardRef()?\n\n' +
|
||||
'Check the render method ' +
|
||||
'of `Parent`.\n' +
|
||||
' in Child (at **)\n' +
|
||||
' in Parent (at **)',
|
||||
@@ -354,7 +360,7 @@ describe('ReactFunctionComponent', () => {
|
||||
}
|
||||
|
||||
expect(() => ReactTestUtils.renderIntoDocument(<Child />)).toWarnDev(
|
||||
'Each child in an array or iterator should have a unique "key" prop.\n\n' +
|
||||
'Each child in a list should have a unique "key" prop.\n\n' +
|
||||
'Check the render method of `Child`.',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -162,8 +162,8 @@ describe('ReactMultiChildText', () => {
|
||||
['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, 'foo'],
|
||||
]);
|
||||
}).toWarnDev([
|
||||
'Warning: Each child in an array or iterator should have a unique "key" prop.',
|
||||
'Warning: Each child in an array or iterator should have a unique "key" prop.',
|
||||
'Warning: Each child in a list should have a unique "key" prop.',
|
||||
'Warning: Each child in a list should have a unique "key" prop.',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
let React;
|
||||
let ReactDOMServer;
|
||||
let PropTypes;
|
||||
let ReactCurrentOwner;
|
||||
let ReactCurrentDispatcher;
|
||||
|
||||
function normalizeCodeLocInfo(str) {
|
||||
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
|
||||
@@ -25,9 +25,9 @@ describe('ReactDOMServer', () => {
|
||||
React = require('react');
|
||||
PropTypes = require('prop-types');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
ReactCurrentOwner =
|
||||
ReactCurrentDispatcher =
|
||||
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
|
||||
.ReactCurrentOwner;
|
||||
.ReactCurrentDispatcher;
|
||||
});
|
||||
|
||||
describe('renderToString', () => {
|
||||
@@ -439,7 +439,7 @@ describe('ReactDOMServer', () => {
|
||||
const Context = React.createContext(0);
|
||||
|
||||
function readContext(context) {
|
||||
return ReactCurrentOwner.currentDispatcher.readContext(context);
|
||||
return ReactCurrentDispatcher.current.readContext(context);
|
||||
}
|
||||
|
||||
function Consumer(props) {
|
||||
@@ -556,6 +556,52 @@ describe('ReactDOMServer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderToNodeStream', () => {
|
||||
it('should generate simple markup', () => {
|
||||
const SuccessfulElement = React.createElement(() => <img />);
|
||||
const response = ReactDOMServer.renderToNodeStream(SuccessfulElement);
|
||||
expect(response.read().toString()).toMatch(
|
||||
new RegExp('<img data-reactroot=""' + '/>'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle errors correctly', () => {
|
||||
const FailingElement = React.createElement(() => {
|
||||
throw new Error('An Error');
|
||||
});
|
||||
const response = ReactDOMServer.renderToNodeStream(FailingElement);
|
||||
return new Promise(resolve => {
|
||||
response.once('error', () => {
|
||||
resolve();
|
||||
});
|
||||
expect(response.read()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderToStaticNodeStream', () => {
|
||||
it('should generate simple markup', () => {
|
||||
const SuccessfulElement = React.createElement(() => <img />);
|
||||
const response = ReactDOMServer.renderToStaticNodeStream(
|
||||
SuccessfulElement,
|
||||
);
|
||||
expect(response.read().toString()).toMatch(new RegExp('<img' + '/>'));
|
||||
});
|
||||
|
||||
it('should handle errors correctly', () => {
|
||||
const FailingElement = React.createElement(() => {
|
||||
throw new Error('An Error');
|
||||
});
|
||||
const response = ReactDOMServer.renderToStaticNodeStream(FailingElement);
|
||||
return new Promise(resolve => {
|
||||
response.once('error', () => {
|
||||
resolve();
|
||||
});
|
||||
expect(response.read()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('warns with a no-op when an async setState is triggered', () => {
|
||||
class Foo extends React.Component {
|
||||
UNSAFE_componentWillMount() {
|
||||
|
||||
@@ -14,6 +14,7 @@ let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMServer;
|
||||
let ReactTestUtils;
|
||||
let act;
|
||||
|
||||
function getTestDocument(markup) {
|
||||
const doc = document.implementation.createHTMLDocument('');
|
||||
@@ -33,6 +34,7 @@ describe('ReactTestUtils', () => {
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
act = ReactTestUtils.act;
|
||||
});
|
||||
|
||||
it('Simulate should have locally attached media events', () => {
|
||||
@@ -515,4 +517,174 @@ describe('ReactTestUtils', () => {
|
||||
ReactTestUtils.renderIntoDocument(<Component />);
|
||||
expect(mockArgs.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('can use act to batch effects', () => {
|
||||
function App(props) {
|
||||
React.useEffect(props.callback);
|
||||
return null;
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
try {
|
||||
let called = false;
|
||||
act(() => {
|
||||
ReactDOM.render(
|
||||
<App
|
||||
callback={() => {
|
||||
called = true;
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
});
|
||||
|
||||
expect(called).toBe(true);
|
||||
} finally {
|
||||
document.body.removeChild(container);
|
||||
}
|
||||
});
|
||||
|
||||
it('flushes effects on every call', () => {
|
||||
function App(props) {
|
||||
let [ctr, setCtr] = React.useState(0);
|
||||
React.useEffect(() => {
|
||||
props.callback(ctr);
|
||||
});
|
||||
return (
|
||||
<button id="button" onClick={() => setCtr(x => x + 1)}>
|
||||
click me!
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let calledCtr = 0;
|
||||
act(() => {
|
||||
ReactDOM.render(
|
||||
<App
|
||||
callback={val => {
|
||||
calledCtr = val;
|
||||
}}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
});
|
||||
const button = document.getElementById('button');
|
||||
function click() {
|
||||
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
||||
}
|
||||
|
||||
act(() => {
|
||||
click();
|
||||
click();
|
||||
click();
|
||||
});
|
||||
expect(calledCtr).toBe(3);
|
||||
act(click);
|
||||
expect(calledCtr).toBe(4);
|
||||
act(click);
|
||||
expect(calledCtr).toBe(5);
|
||||
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
it('can use act to batch effects on updates too', () => {
|
||||
function App() {
|
||||
let [ctr, setCtr] = React.useState(0);
|
||||
return (
|
||||
<button id="button" onClick={() => setCtr(x => x + 1)}>
|
||||
{ctr}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let button;
|
||||
act(() => {
|
||||
ReactDOM.render(<App />, container);
|
||||
});
|
||||
button = document.getElementById('button');
|
||||
expect(button.innerHTML).toBe('0');
|
||||
act(() => {
|
||||
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
||||
});
|
||||
expect(button.innerHTML).toBe('1');
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
it('detects setState being called outside of act(...)', () => {
|
||||
let setValueRef = null;
|
||||
function App() {
|
||||
let [value, setValue] = React.useState(0);
|
||||
setValueRef = setValue;
|
||||
return (
|
||||
<button id="button" onClick={() => setValue(2)}>
|
||||
{value}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
let button;
|
||||
act(() => {
|
||||
ReactDOM.render(<App />, container);
|
||||
button = container.querySelector('#button');
|
||||
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
|
||||
});
|
||||
expect(button.innerHTML).toBe('2');
|
||||
expect(() => setValueRef(1)).toWarnDev(
|
||||
['An update to App inside a test was not wrapped in act(...).'],
|
||||
{withoutStack: 1},
|
||||
);
|
||||
document.body.removeChild(container);
|
||||
});
|
||||
|
||||
it('lets a ticker update', () => {
|
||||
function App() {
|
||||
let [toggle, setToggle] = React.useState(0);
|
||||
React.useEffect(() => {
|
||||
let timeout = setTimeout(() => {
|
||||
setToggle(1);
|
||||
}, 200);
|
||||
return () => clearTimeout(timeout);
|
||||
});
|
||||
return toggle;
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
|
||||
act(() => {
|
||||
act(() => {
|
||||
ReactDOM.render(<App />, container);
|
||||
});
|
||||
jest.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
expect(container.innerHTML).toBe('1');
|
||||
});
|
||||
|
||||
it('warns if you return a value inside act', () => {
|
||||
expect(() => act(() => null)).toWarnDev(
|
||||
[
|
||||
'The callback passed to ReactTestUtils.act(...) function must not return anything.',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(() => act(() => 123)).toWarnDev(
|
||||
[
|
||||
'The callback passed to ReactTestUtils.act(...) function must not return anything.',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
|
||||
it('warns if you try to await an .act call', () => {
|
||||
expect(act(() => {}).then).toWarnDev(
|
||||
[
|
||||
'Do not await the result of calling ReactTestUtils.act(...), it is not a Promise.',
|
||||
],
|
||||
{withoutStack: true},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
207
packages/react-dom/src/client/ReactDOM.js
vendored
207
packages/react-dom/src/client/ReactDOM.js
vendored
@@ -14,20 +14,46 @@ import type {
|
||||
FiberRoot,
|
||||
Batch as FiberRootBatch,
|
||||
} from 'react-reconciler/src/ReactFiberRoot';
|
||||
import type {Container} from './ReactDOMHostConfig';
|
||||
|
||||
import '../shared/checkReact';
|
||||
import './ReactDOMClientInjection';
|
||||
|
||||
import * as DOMRenderer from 'react-reconciler/inline.dom';
|
||||
import * as ReactPortal from 'shared/ReactPortal';
|
||||
import {
|
||||
computeUniqueAsyncExpiration,
|
||||
findHostInstanceWithNoPortals,
|
||||
updateContainerAtExpirationTime,
|
||||
flushRoot,
|
||||
createContainer,
|
||||
updateContainer,
|
||||
batchedUpdates,
|
||||
unbatchedUpdates,
|
||||
interactiveUpdates,
|
||||
flushInteractiveUpdates,
|
||||
flushSync,
|
||||
flushControlled,
|
||||
injectIntoDevTools,
|
||||
getPublicRootInstance,
|
||||
findHostInstance,
|
||||
findHostInstanceWithWarning,
|
||||
} from 'react-reconciler/inline.dom';
|
||||
import {createPortal as createPortalImpl} from 'shared/ReactPortal';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import * as ReactGenericBatching from 'events/ReactGenericBatching';
|
||||
import * as ReactControlledComponent from 'events/ReactControlledComponent';
|
||||
import * as EventPluginHub from 'events/EventPluginHub';
|
||||
import * as EventPluginRegistry from 'events/EventPluginRegistry';
|
||||
import * as EventPropagators from 'events/EventPropagators';
|
||||
import * as ReactInstanceMap from 'shared/ReactInstanceMap';
|
||||
import {setBatchingImplementation} from 'events/ReactGenericBatching';
|
||||
import {
|
||||
setRestoreImplementation,
|
||||
enqueueStateRestore,
|
||||
restoreStateIfNeeded,
|
||||
} from 'events/ReactControlledComponent';
|
||||
import {
|
||||
injection as EventPluginHubInjection,
|
||||
runEventsInBatch,
|
||||
} from 'events/EventPluginHub';
|
||||
import {eventNameDispatchConfigs} from 'events/EventPluginRegistry';
|
||||
import {
|
||||
accumulateTwoPhaseDispatches,
|
||||
accumulateDirectDispatches,
|
||||
} from 'events/EventPropagators';
|
||||
import {has as hasInstance} from 'shared/ReactInstanceMap';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
@@ -36,9 +62,14 @@ import lowPriorityWarning from 'shared/lowPriorityWarning';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import * as ReactDOMComponentTree from './ReactDOMComponentTree';
|
||||
import {
|
||||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
getFiberCurrentPropsFromNode,
|
||||
getClosestInstanceFromNode,
|
||||
} from './ReactDOMComponentTree';
|
||||
import {restoreControlledState} from './ReactDOMComponent';
|
||||
import * as ReactDOMEventListener from '../events/ReactDOMEventListener';
|
||||
import {dispatchEvent} from '../events/ReactDOMEventListener';
|
||||
import {
|
||||
ELEMENT_NODE,
|
||||
COMMENT_NODE,
|
||||
@@ -74,7 +105,7 @@ if (__DEV__) {
|
||||
|
||||
topLevelUpdateWarnings = (container: DOMContainer) => {
|
||||
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
|
||||
const hostInstance = DOMRenderer.findHostInstanceWithNoPortals(
|
||||
const hostInstance = findHostInstanceWithNoPortals(
|
||||
container._reactRootContainer._internalRoot.current,
|
||||
);
|
||||
if (hostInstance) {
|
||||
@@ -90,9 +121,7 @@ if (__DEV__) {
|
||||
|
||||
const isRootRenderedBySomeReact = !!container._reactRootContainer;
|
||||
const rootEl = getReactRootElementInContainer(container);
|
||||
const hasNonRootReactChild = !!(
|
||||
rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl)
|
||||
);
|
||||
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));
|
||||
|
||||
warningWithoutStack(
|
||||
!hasNonRootReactChild || isRootRenderedBySomeReact,
|
||||
@@ -125,14 +154,16 @@ if (__DEV__) {
|
||||
};
|
||||
}
|
||||
|
||||
ReactControlledComponent.setRestoreImplementation(restoreControlledState);
|
||||
setRestoreImplementation(restoreControlledState);
|
||||
|
||||
export type DOMContainer =
|
||||
| (Element & {
|
||||
_reactRootContainer: ?Root,
|
||||
_reactHasBeenPassedToCreateRootDEV: ?boolean,
|
||||
})
|
||||
| (Document & {
|
||||
_reactRootContainer: ?Root,
|
||||
_reactHasBeenPassedToCreateRootDEV: ?boolean,
|
||||
});
|
||||
|
||||
type Batch = FiberRootBatch & {
|
||||
@@ -165,7 +196,7 @@ type Root = {
|
||||
};
|
||||
|
||||
function ReactBatch(root: ReactRoot) {
|
||||
const expirationTime = DOMRenderer.computeUniqueAsyncExpiration();
|
||||
const expirationTime = computeUniqueAsyncExpiration();
|
||||
this._expirationTime = expirationTime;
|
||||
this._root = root;
|
||||
this._next = null;
|
||||
@@ -185,7 +216,7 @@ ReactBatch.prototype.render = function(children: ReactNodeList) {
|
||||
const internalRoot = this._root._internalRoot;
|
||||
const expirationTime = this._expirationTime;
|
||||
const work = new ReactWork();
|
||||
DOMRenderer.updateContainerAtExpirationTime(
|
||||
updateContainerAtExpirationTime(
|
||||
children,
|
||||
internalRoot,
|
||||
null,
|
||||
@@ -256,7 +287,7 @@ ReactBatch.prototype.commit = function() {
|
||||
|
||||
// Synchronously flush all the work up to this batch's expiration time.
|
||||
this._defer = false;
|
||||
DOMRenderer.flushRoot(internalRoot, expirationTime);
|
||||
flushRoot(internalRoot, expirationTime);
|
||||
|
||||
// Pop the batch from the list.
|
||||
const next = this._next;
|
||||
@@ -332,11 +363,11 @@ ReactWork.prototype._onCommit = function(): void {
|
||||
};
|
||||
|
||||
function ReactRoot(
|
||||
container: Container,
|
||||
container: DOMContainer,
|
||||
isConcurrent: boolean,
|
||||
hydrate: boolean,
|
||||
) {
|
||||
const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);
|
||||
const root = createContainer(container, isConcurrent, hydrate);
|
||||
this._internalRoot = root;
|
||||
}
|
||||
ReactRoot.prototype.render = function(
|
||||
@@ -352,7 +383,7 @@ ReactRoot.prototype.render = function(
|
||||
if (callback !== null) {
|
||||
work.then(callback);
|
||||
}
|
||||
DOMRenderer.updateContainer(children, root, null, work._onCommit);
|
||||
updateContainer(children, root, null, work._onCommit);
|
||||
return work;
|
||||
};
|
||||
ReactRoot.prototype.unmount = function(callback: ?() => mixed): Work {
|
||||
@@ -365,7 +396,7 @@ ReactRoot.prototype.unmount = function(callback: ?() => mixed): Work {
|
||||
if (callback !== null) {
|
||||
work.then(callback);
|
||||
}
|
||||
DOMRenderer.updateContainer(null, root, null, work._onCommit);
|
||||
updateContainer(null, root, null, work._onCommit);
|
||||
return work;
|
||||
};
|
||||
ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
|
||||
@@ -382,7 +413,7 @@ ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
|
||||
if (callback !== null) {
|
||||
work.then(callback);
|
||||
}
|
||||
DOMRenderer.updateContainer(children, root, parentComponent, work._onCommit);
|
||||
updateContainer(children, root, parentComponent, work._onCommit);
|
||||
return work;
|
||||
};
|
||||
ReactRoot.prototype.createBatch = function(): Batch {
|
||||
@@ -453,10 +484,10 @@ function shouldHydrateDueToLegacyHeuristic(container) {
|
||||
);
|
||||
}
|
||||
|
||||
ReactGenericBatching.setBatchingImplementation(
|
||||
DOMRenderer.batchedUpdates,
|
||||
DOMRenderer.interactiveUpdates,
|
||||
DOMRenderer.flushInteractiveUpdates,
|
||||
setBatchingImplementation(
|
||||
batchedUpdates,
|
||||
interactiveUpdates,
|
||||
flushInteractiveUpdates,
|
||||
);
|
||||
|
||||
let warnedAboutHydrateAPI = false;
|
||||
@@ -513,12 +544,6 @@ function legacyRenderSubtreeIntoContainer(
|
||||
forceHydrate: boolean,
|
||||
callback: ?Function,
|
||||
) {
|
||||
// TODO: Ensure all entry points contain this check
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
topLevelUpdateWarnings(container);
|
||||
}
|
||||
@@ -535,12 +560,12 @@ function legacyRenderSubtreeIntoContainer(
|
||||
if (typeof callback === 'function') {
|
||||
const originalCallback = callback;
|
||||
callback = function() {
|
||||
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
|
||||
const instance = getPublicRootInstance(root._internalRoot);
|
||||
originalCallback.call(instance);
|
||||
};
|
||||
}
|
||||
// Initial mount should not be batched.
|
||||
DOMRenderer.unbatchedUpdates(() => {
|
||||
unbatchedUpdates(() => {
|
||||
if (parentComponent != null) {
|
||||
root.legacy_renderSubtreeIntoContainer(
|
||||
parentComponent,
|
||||
@@ -555,7 +580,7 @@ function legacyRenderSubtreeIntoContainer(
|
||||
if (typeof callback === 'function') {
|
||||
const originalCallback = callback;
|
||||
callback = function() {
|
||||
const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);
|
||||
const instance = getPublicRootInstance(root._internalRoot);
|
||||
originalCallback.call(instance);
|
||||
};
|
||||
}
|
||||
@@ -570,7 +595,7 @@ function legacyRenderSubtreeIntoContainer(
|
||||
root.render(children, callback);
|
||||
}
|
||||
}
|
||||
return DOMRenderer.getPublicRootInstance(root._internalRoot);
|
||||
return getPublicRootInstance(root._internalRoot);
|
||||
}
|
||||
|
||||
function createPortal(
|
||||
@@ -583,7 +608,7 @@ function createPortal(
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
// TODO: pass ReactDOM portal implementation as third argument
|
||||
return ReactPortal.createPortal(children, container, null, key);
|
||||
return createPortalImpl(children, container, null, key);
|
||||
}
|
||||
|
||||
const ReactDOM: Object = {
|
||||
@@ -616,15 +641,25 @@ const ReactDOM: Object = {
|
||||
return (componentOrElement: any);
|
||||
}
|
||||
if (__DEV__) {
|
||||
return DOMRenderer.findHostInstanceWithWarning(
|
||||
componentOrElement,
|
||||
'findDOMNode',
|
||||
);
|
||||
return findHostInstanceWithWarning(componentOrElement, 'findDOMNode');
|
||||
}
|
||||
return DOMRenderer.findHostInstance(componentOrElement);
|
||||
return findHostInstance(componentOrElement);
|
||||
},
|
||||
|
||||
hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactHasBeenPassedToCreateRootDEV,
|
||||
'You are calling ReactDOM.hydrate() on a container that was previously ' +
|
||||
'passed to ReactDOM.%s(). This is not supported. ' +
|
||||
'Did you mean to call root.render(element, {hydrate: true})?',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
}
|
||||
// TODO: throw or warn if we couldn't hydrate?
|
||||
return legacyRenderSubtreeIntoContainer(
|
||||
null,
|
||||
@@ -640,6 +675,19 @@ const ReactDOM: Object = {
|
||||
container: DOMContainer,
|
||||
callback: ?Function,
|
||||
) {
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactHasBeenPassedToCreateRootDEV,
|
||||
'You are calling ReactDOM.render() on a container that was previously ' +
|
||||
'passed to ReactDOM.%s(). This is not supported. ' +
|
||||
'Did you mean to call root.render(element)?',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
}
|
||||
return legacyRenderSubtreeIntoContainer(
|
||||
null,
|
||||
element,
|
||||
@@ -656,7 +704,11 @@ const ReactDOM: Object = {
|
||||
callback: ?Function,
|
||||
) {
|
||||
invariant(
|
||||
parentComponent != null && ReactInstanceMap.has(parentComponent),
|
||||
isValidContainer(containerNode),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
invariant(
|
||||
parentComponent != null && hasInstance(parentComponent),
|
||||
'parentComponent must be a valid React Component',
|
||||
);
|
||||
return legacyRenderSubtreeIntoContainer(
|
||||
@@ -674,11 +726,19 @@ const ReactDOM: Object = {
|
||||
'unmountComponentAtNode(...): Target container is not a DOM element.',
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactHasBeenPassedToCreateRootDEV,
|
||||
'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' +
|
||||
'passed to ReactDOM.%s(). This is not supported. Did you mean to call root.unmount()?',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
}
|
||||
|
||||
if (container._reactRootContainer) {
|
||||
if (__DEV__) {
|
||||
const rootEl = getReactRootElementInContainer(container);
|
||||
const renderedByDifferentReact =
|
||||
rootEl && !ReactDOMComponentTree.getInstanceFromNode(rootEl);
|
||||
const renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl);
|
||||
warningWithoutStack(
|
||||
!renderedByDifferentReact,
|
||||
"unmountComponentAtNode(): The node you're attempting to unmount " +
|
||||
@@ -687,7 +747,7 @@ const ReactDOM: Object = {
|
||||
}
|
||||
|
||||
// Unmount should not be batched.
|
||||
DOMRenderer.unbatchedUpdates(() => {
|
||||
unbatchedUpdates(() => {
|
||||
legacyRenderSubtreeIntoContainer(null, null, container, false, () => {
|
||||
container._reactRootContainer = null;
|
||||
});
|
||||
@@ -698,9 +758,7 @@ const ReactDOM: Object = {
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
const rootEl = getReactRootElementInContainer(container);
|
||||
const hasNonRootReactChild = !!(
|
||||
rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl)
|
||||
);
|
||||
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));
|
||||
|
||||
// Check if the container itself is a React root node.
|
||||
const isContainerReactRoot =
|
||||
@@ -740,29 +798,30 @@ const ReactDOM: Object = {
|
||||
return createPortal(...args);
|
||||
},
|
||||
|
||||
unstable_batchedUpdates: DOMRenderer.batchedUpdates,
|
||||
unstable_batchedUpdates: batchedUpdates,
|
||||
|
||||
unstable_interactiveUpdates: DOMRenderer.interactiveUpdates,
|
||||
unstable_interactiveUpdates: interactiveUpdates,
|
||||
|
||||
flushSync: DOMRenderer.flushSync,
|
||||
flushSync: flushSync,
|
||||
|
||||
unstable_flushControlled: DOMRenderer.flushControlled,
|
||||
unstable_createRoot: createRoot,
|
||||
unstable_flushControlled: flushControlled,
|
||||
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
|
||||
// Keep in sync with ReactDOMUnstableNativeDependencies.js
|
||||
// and ReactTestUtils.js. This is an array for better minification.
|
||||
Events: [
|
||||
ReactDOMComponentTree.getInstanceFromNode,
|
||||
ReactDOMComponentTree.getNodeFromInstance,
|
||||
ReactDOMComponentTree.getFiberCurrentPropsFromNode,
|
||||
EventPluginHub.injection.injectEventPluginsByName,
|
||||
EventPluginRegistry.eventNameDispatchConfigs,
|
||||
EventPropagators.accumulateTwoPhaseDispatches,
|
||||
EventPropagators.accumulateDirectDispatches,
|
||||
ReactControlledComponent.enqueueStateRestore,
|
||||
ReactControlledComponent.restoreStateIfNeeded,
|
||||
ReactDOMEventListener.dispatchEvent,
|
||||
EventPluginHub.runEventsInBatch,
|
||||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
getFiberCurrentPropsFromNode,
|
||||
EventPluginHubInjection.injectEventPluginsByName,
|
||||
eventNameDispatchConfigs,
|
||||
accumulateTwoPhaseDispatches,
|
||||
accumulateDirectDispatches,
|
||||
enqueueStateRestore,
|
||||
restoreStateIfNeeded,
|
||||
dispatchEvent,
|
||||
runEventsInBatch,
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -780,18 +839,26 @@ function createRoot(container: DOMContainer, options?: RootOptions): ReactRoot {
|
||||
'%s(...): Target container is not a DOM element.',
|
||||
functionName,
|
||||
);
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactRootContainer,
|
||||
'You are calling ReactDOM.%s() on a container that was previously ' +
|
||||
'passed to ReactDOM.render(). This is not supported.',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
container._reactHasBeenPassedToCreateRootDEV = true;
|
||||
}
|
||||
const hydrate = options != null && options.hydrate === true;
|
||||
return new ReactRoot(container, true, hydrate);
|
||||
}
|
||||
|
||||
if (enableStableConcurrentModeAPIs) {
|
||||
ReactDOM.createRoot = createRoot;
|
||||
} else {
|
||||
ReactDOM.unstable_createRoot = createRoot;
|
||||
ReactDOM.unstable_createRoot = undefined;
|
||||
}
|
||||
|
||||
const foundDevTools = DOMRenderer.injectIntoDevTools({
|
||||
findFiberByHostInstance: ReactDOMComponentTree.getClosestInstanceFromNode,
|
||||
const foundDevTools = injectIntoDevTools({
|
||||
findFiberByHostInstance: getClosestInstanceFromNode,
|
||||
bundleType: __DEV__ ? 1 : 0,
|
||||
version: ReactVersion,
|
||||
rendererPackageName: 'react-dom',
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as EventPluginHub from 'events/EventPluginHub';
|
||||
import * as EventPluginUtils from 'events/EventPluginUtils';
|
||||
import {injection as EventPluginHubInjection} from 'events/EventPluginHub';
|
||||
import {setComponentTree} from 'events/EventPluginUtils';
|
||||
|
||||
import {
|
||||
getFiberCurrentPropsFromNode,
|
||||
@@ -23,8 +23,8 @@ import SimpleEventPlugin from '../events/SimpleEventPlugin';
|
||||
/**
|
||||
* Inject modules for resolving DOM hierarchy and plugin ordering.
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginOrder(DOMEventPluginOrder);
|
||||
EventPluginUtils.setComponentTree(
|
||||
EventPluginHubInjection.injectEventPluginOrder(DOMEventPluginOrder);
|
||||
setComponentTree(
|
||||
getFiberCurrentPropsFromNode,
|
||||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
@@ -34,7 +34,7 @@ EventPluginUtils.setComponentTree(
|
||||
* Some important event plugins included by default (without having to require
|
||||
* them).
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginsByName({
|
||||
EventPluginHubInjection.injectEventPluginsByName({
|
||||
SimpleEventPlugin: SimpleEventPlugin,
|
||||
EnterLeaveEventPlugin: EnterLeaveEventPlugin,
|
||||
ChangeEventPlugin: ChangeEventPlugin,
|
||||
|
||||
158
packages/react-dom/src/client/ReactDOMComponent.js
vendored
158
packages/react-dom/src/client/ReactDOMComponent.js
vendored
@@ -14,12 +14,39 @@ import warning from 'shared/warning';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
|
||||
import * as DOMPropertyOperations from './DOMPropertyOperations';
|
||||
import * as ReactDOMInput from './ReactDOMInput';
|
||||
import * as ReactDOMOption from './ReactDOMOption';
|
||||
import * as ReactDOMSelect from './ReactDOMSelect';
|
||||
import * as ReactDOMTextarea from './ReactDOMTextarea';
|
||||
import * as inputValueTracking from './inputValueTracking';
|
||||
import {
|
||||
getValueForAttribute,
|
||||
getValueForProperty,
|
||||
setValueForProperty,
|
||||
} from './DOMPropertyOperations';
|
||||
import {
|
||||
initWrapperState as ReactDOMInputInitWrapperState,
|
||||
getHostProps as ReactDOMInputGetHostProps,
|
||||
postMountWrapper as ReactDOMInputPostMountWrapper,
|
||||
updateChecked as ReactDOMInputUpdateChecked,
|
||||
updateWrapper as ReactDOMInputUpdateWrapper,
|
||||
restoreControlledState as ReactDOMInputRestoreControlledState,
|
||||
} from './ReactDOMInput';
|
||||
import {
|
||||
getHostProps as ReactDOMOptionGetHostProps,
|
||||
postMountWrapper as ReactDOMOptionPostMountWrapper,
|
||||
validateProps as ReactDOMOptionValidateProps,
|
||||
} from './ReactDOMOption';
|
||||
import {
|
||||
initWrapperState as ReactDOMSelectInitWrapperState,
|
||||
getHostProps as ReactDOMSelectGetHostProps,
|
||||
postMountWrapper as ReactDOMSelectPostMountWrapper,
|
||||
restoreControlledState as ReactDOMSelectRestoreControlledState,
|
||||
postUpdateWrapper as ReactDOMSelectPostUpdateWrapper,
|
||||
} from './ReactDOMSelect';
|
||||
import {
|
||||
initWrapperState as ReactDOMTextareaInitWrapperState,
|
||||
getHostProps as ReactDOMTextareaGetHostProps,
|
||||
postMountWrapper as ReactDOMTextareaPostMountWrapper,
|
||||
updateWrapper as ReactDOMTextareaUpdateWrapper,
|
||||
restoreControlledState as ReactDOMTextareaRestoreControlledState,
|
||||
} from './ReactDOMTextarea';
|
||||
import {track} from './inputValueTracking';
|
||||
import setInnerHTML from './setInnerHTML';
|
||||
import setTextContent from './setTextContent';
|
||||
import {
|
||||
@@ -32,7 +59,11 @@ import {
|
||||
} from '../events/DOMTopLevelEventTypes';
|
||||
import {listenTo, trapBubbledEvent} from '../events/ReactBrowserEventEmitter';
|
||||
import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
|
||||
import * as CSSPropertyOperations from '../shared/CSSPropertyOperations';
|
||||
import {
|
||||
createDangerousStringForStyles,
|
||||
setValueForStyles,
|
||||
validateShorthandPropertyCollisionInDev,
|
||||
} from '../shared/CSSPropertyOperations';
|
||||
import {Namespaces, getIntrinsicNamespace} from '../shared/DOMNamespaces';
|
||||
import {
|
||||
getPropertyInfo,
|
||||
@@ -276,7 +307,7 @@ function setInitialDOMProperties(
|
||||
}
|
||||
}
|
||||
// Relies on `updateStylesByID` not mutating `styleUpdates`.
|
||||
CSSPropertyOperations.setValueForStyles(domElement, nextProp);
|
||||
setValueForStyles(domElement, nextProp);
|
||||
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
||||
const nextHtml = nextProp ? nextProp[HTML] : undefined;
|
||||
if (nextHtml != null) {
|
||||
@@ -313,12 +344,7 @@ function setInitialDOMProperties(
|
||||
ensureListeningTo(rootContainerElement, propKey);
|
||||
}
|
||||
} else if (nextProp != null) {
|
||||
DOMPropertyOperations.setValueForProperty(
|
||||
domElement,
|
||||
propKey,
|
||||
nextProp,
|
||||
isCustomComponentTag,
|
||||
);
|
||||
setValueForProperty(domElement, propKey, nextProp, isCustomComponentTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,18 +360,13 @@ function updateDOMProperties(
|
||||
const propKey = updatePayload[i];
|
||||
const propValue = updatePayload[i + 1];
|
||||
if (propKey === STYLE) {
|
||||
CSSPropertyOperations.setValueForStyles(domElement, propValue);
|
||||
setValueForStyles(domElement, propValue);
|
||||
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
||||
setInnerHTML(domElement, propValue);
|
||||
} else if (propKey === CHILDREN) {
|
||||
setTextContent(domElement, propValue);
|
||||
} else {
|
||||
DOMPropertyOperations.setValueForProperty(
|
||||
domElement,
|
||||
propKey,
|
||||
propValue,
|
||||
isCustomComponentTag,
|
||||
);
|
||||
setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,28 +526,28 @@ export function setInitialProperties(
|
||||
props = rawProps;
|
||||
break;
|
||||
case 'input':
|
||||
ReactDOMInput.initWrapperState(domElement, rawProps);
|
||||
props = ReactDOMInput.getHostProps(domElement, rawProps);
|
||||
ReactDOMInputInitWrapperState(domElement, rawProps);
|
||||
props = ReactDOMInputGetHostProps(domElement, rawProps);
|
||||
trapBubbledEvent(TOP_INVALID, domElement);
|
||||
// For controlled components we always need to ensure we're listening
|
||||
// to onChange. Even if there is no listener.
|
||||
ensureListeningTo(rootContainerElement, 'onChange');
|
||||
break;
|
||||
case 'option':
|
||||
ReactDOMOption.validateProps(domElement, rawProps);
|
||||
props = ReactDOMOption.getHostProps(domElement, rawProps);
|
||||
ReactDOMOptionValidateProps(domElement, rawProps);
|
||||
props = ReactDOMOptionGetHostProps(domElement, rawProps);
|
||||
break;
|
||||
case 'select':
|
||||
ReactDOMSelect.initWrapperState(domElement, rawProps);
|
||||
props = ReactDOMSelect.getHostProps(domElement, rawProps);
|
||||
ReactDOMSelectInitWrapperState(domElement, rawProps);
|
||||
props = ReactDOMSelectGetHostProps(domElement, rawProps);
|
||||
trapBubbledEvent(TOP_INVALID, domElement);
|
||||
// For controlled components we always need to ensure we're listening
|
||||
// to onChange. Even if there is no listener.
|
||||
ensureListeningTo(rootContainerElement, 'onChange');
|
||||
break;
|
||||
case 'textarea':
|
||||
ReactDOMTextarea.initWrapperState(domElement, rawProps);
|
||||
props = ReactDOMTextarea.getHostProps(domElement, rawProps);
|
||||
ReactDOMTextareaInitWrapperState(domElement, rawProps);
|
||||
props = ReactDOMTextareaGetHostProps(domElement, rawProps);
|
||||
trapBubbledEvent(TOP_INVALID, domElement);
|
||||
// For controlled components we always need to ensure we're listening
|
||||
// to onChange. Even if there is no listener.
|
||||
@@ -550,20 +571,20 @@ export function setInitialProperties(
|
||||
case 'input':
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
// up necessary since we never stop tracking anymore.
|
||||
inputValueTracking.track((domElement: any));
|
||||
ReactDOMInput.postMountWrapper(domElement, rawProps, false);
|
||||
track((domElement: any));
|
||||
ReactDOMInputPostMountWrapper(domElement, rawProps, false);
|
||||
break;
|
||||
case 'textarea':
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
// up necessary since we never stop tracking anymore.
|
||||
inputValueTracking.track((domElement: any));
|
||||
ReactDOMTextarea.postMountWrapper(domElement, rawProps);
|
||||
track((domElement: any));
|
||||
ReactDOMTextareaPostMountWrapper(domElement, rawProps);
|
||||
break;
|
||||
case 'option':
|
||||
ReactDOMOption.postMountWrapper(domElement, rawProps);
|
||||
ReactDOMOptionPostMountWrapper(domElement, rawProps);
|
||||
break;
|
||||
case 'select':
|
||||
ReactDOMSelect.postMountWrapper(domElement, rawProps);
|
||||
ReactDOMSelectPostMountWrapper(domElement, rawProps);
|
||||
break;
|
||||
default:
|
||||
if (typeof props.onClick === 'function') {
|
||||
@@ -592,23 +613,23 @@ export function diffProperties(
|
||||
let nextProps: Object;
|
||||
switch (tag) {
|
||||
case 'input':
|
||||
lastProps = ReactDOMInput.getHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMInput.getHostProps(domElement, nextRawProps);
|
||||
lastProps = ReactDOMInputGetHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMInputGetHostProps(domElement, nextRawProps);
|
||||
updatePayload = [];
|
||||
break;
|
||||
case 'option':
|
||||
lastProps = ReactDOMOption.getHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMOption.getHostProps(domElement, nextRawProps);
|
||||
lastProps = ReactDOMOptionGetHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMOptionGetHostProps(domElement, nextRawProps);
|
||||
updatePayload = [];
|
||||
break;
|
||||
case 'select':
|
||||
lastProps = ReactDOMSelect.getHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMSelect.getHostProps(domElement, nextRawProps);
|
||||
lastProps = ReactDOMSelectGetHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMSelectGetHostProps(domElement, nextRawProps);
|
||||
updatePayload = [];
|
||||
break;
|
||||
case 'textarea':
|
||||
lastProps = ReactDOMTextarea.getHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMTextarea.getHostProps(domElement, nextRawProps);
|
||||
lastProps = ReactDOMTextareaGetHostProps(domElement, lastRawProps);
|
||||
nextProps = ReactDOMTextareaGetHostProps(domElement, nextRawProps);
|
||||
updatePayload = [];
|
||||
break;
|
||||
default:
|
||||
@@ -766,6 +787,9 @@ export function diffProperties(
|
||||
}
|
||||
}
|
||||
if (styleUpdates) {
|
||||
if (__DEV__) {
|
||||
validateShorthandPropertyCollisionInDev(styleUpdates, nextProps[STYLE]);
|
||||
}
|
||||
(updatePayload = updatePayload || []).push(STYLE, styleUpdates);
|
||||
}
|
||||
return updatePayload;
|
||||
@@ -787,7 +811,7 @@ export function updateProperties(
|
||||
nextRawProps.type === 'radio' &&
|
||||
nextRawProps.name != null
|
||||
) {
|
||||
ReactDOMInput.updateChecked(domElement, nextRawProps);
|
||||
ReactDOMInputUpdateChecked(domElement, nextRawProps);
|
||||
}
|
||||
|
||||
const wasCustomComponentTag = isCustomComponent(tag, lastRawProps);
|
||||
@@ -807,15 +831,15 @@ export function updateProperties(
|
||||
// Update the wrapper around inputs *after* updating props. This has to
|
||||
// happen after `updateDOMProperties`. Otherwise HTML5 input validations
|
||||
// raise warnings and prevent the new value from being assigned.
|
||||
ReactDOMInput.updateWrapper(domElement, nextRawProps);
|
||||
ReactDOMInputUpdateWrapper(domElement, nextRawProps);
|
||||
break;
|
||||
case 'textarea':
|
||||
ReactDOMTextarea.updateWrapper(domElement, nextRawProps);
|
||||
ReactDOMTextareaUpdateWrapper(domElement, nextRawProps);
|
||||
break;
|
||||
case 'select':
|
||||
// <select> value update needs to occur after <option> children
|
||||
// reconciliation
|
||||
ReactDOMSelect.postUpdateWrapper(domElement, nextRawProps);
|
||||
ReactDOMSelectPostUpdateWrapper(domElement, nextRawProps);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -890,24 +914,24 @@ export function diffHydratedProperties(
|
||||
trapBubbledEvent(TOP_TOGGLE, domElement);
|
||||
break;
|
||||
case 'input':
|
||||
ReactDOMInput.initWrapperState(domElement, rawProps);
|
||||
ReactDOMInputInitWrapperState(domElement, rawProps);
|
||||
trapBubbledEvent(TOP_INVALID, domElement);
|
||||
// For controlled components we always need to ensure we're listening
|
||||
// to onChange. Even if there is no listener.
|
||||
ensureListeningTo(rootContainerElement, 'onChange');
|
||||
break;
|
||||
case 'option':
|
||||
ReactDOMOption.validateProps(domElement, rawProps);
|
||||
ReactDOMOptionValidateProps(domElement, rawProps);
|
||||
break;
|
||||
case 'select':
|
||||
ReactDOMSelect.initWrapperState(domElement, rawProps);
|
||||
ReactDOMSelectInitWrapperState(domElement, rawProps);
|
||||
trapBubbledEvent(TOP_INVALID, domElement);
|
||||
// For controlled components we always need to ensure we're listening
|
||||
// to onChange. Even if there is no listener.
|
||||
ensureListeningTo(rootContainerElement, 'onChange');
|
||||
break;
|
||||
case 'textarea':
|
||||
ReactDOMTextarea.initWrapperState(domElement, rawProps);
|
||||
ReactDOMTextareaInitWrapperState(domElement, rawProps);
|
||||
trapBubbledEvent(TOP_INVALID, domElement);
|
||||
// For controlled components we always need to ensure we're listening
|
||||
// to onChange. Even if there is no listener.
|
||||
@@ -1015,9 +1039,7 @@ export function diffHydratedProperties(
|
||||
extraAttributeNames.delete(propKey);
|
||||
|
||||
if (canDiffStyleForHydrationWarning) {
|
||||
const expectedStyle = CSSPropertyOperations.createDangerousStringForStyles(
|
||||
nextProp,
|
||||
);
|
||||
const expectedStyle = createDangerousStringForStyles(nextProp);
|
||||
serverValue = domElement.getAttribute('style');
|
||||
if (expectedStyle !== serverValue) {
|
||||
warnForPropDifference(propKey, serverValue, expectedStyle);
|
||||
@@ -1026,11 +1048,7 @@ export function diffHydratedProperties(
|
||||
} else if (isCustomComponentTag) {
|
||||
// $FlowFixMe - Should be inferred as not undefined.
|
||||
extraAttributeNames.delete(propKey.toLowerCase());
|
||||
serverValue = DOMPropertyOperations.getValueForAttribute(
|
||||
domElement,
|
||||
propKey,
|
||||
nextProp,
|
||||
);
|
||||
serverValue = getValueForAttribute(domElement, propKey, nextProp);
|
||||
|
||||
if (nextProp !== serverValue) {
|
||||
warnForPropDifference(propKey, serverValue, nextProp);
|
||||
@@ -1048,7 +1066,7 @@ export function diffHydratedProperties(
|
||||
if (propertyInfo !== null) {
|
||||
// $FlowFixMe - Should be inferred as not undefined.
|
||||
extraAttributeNames.delete(propertyInfo.attributeName);
|
||||
serverValue = DOMPropertyOperations.getValueForProperty(
|
||||
serverValue = getValueForProperty(
|
||||
domElement,
|
||||
propKey,
|
||||
nextProp,
|
||||
@@ -1077,11 +1095,7 @@ export function diffHydratedProperties(
|
||||
// $FlowFixMe - Should be inferred as not undefined.
|
||||
extraAttributeNames.delete(propKey);
|
||||
}
|
||||
serverValue = DOMPropertyOperations.getValueForAttribute(
|
||||
domElement,
|
||||
propKey,
|
||||
nextProp,
|
||||
);
|
||||
serverValue = getValueForAttribute(domElement, propKey, nextProp);
|
||||
}
|
||||
|
||||
if (nextProp !== serverValue && !isMismatchDueToBadCasing) {
|
||||
@@ -1103,14 +1117,14 @@ export function diffHydratedProperties(
|
||||
case 'input':
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
// up necessary since we never stop tracking anymore.
|
||||
inputValueTracking.track((domElement: any));
|
||||
ReactDOMInput.postMountWrapper(domElement, rawProps, true);
|
||||
track((domElement: any));
|
||||
ReactDOMInputPostMountWrapper(domElement, rawProps, true);
|
||||
break;
|
||||
case 'textarea':
|
||||
// TODO: Make sure we check if this is still unmounted or do any clean
|
||||
// up necessary since we never stop tracking anymore.
|
||||
inputValueTracking.track((domElement: any));
|
||||
ReactDOMTextarea.postMountWrapper(domElement, rawProps);
|
||||
track((domElement: any));
|
||||
ReactDOMTextareaPostMountWrapper(domElement, rawProps);
|
||||
break;
|
||||
case 'select':
|
||||
case 'option':
|
||||
@@ -1229,13 +1243,13 @@ export function restoreControlledState(
|
||||
): void {
|
||||
switch (tag) {
|
||||
case 'input':
|
||||
ReactDOMInput.restoreControlledState(domElement, props);
|
||||
ReactDOMInputRestoreControlledState(domElement, props);
|
||||
return;
|
||||
case 'textarea':
|
||||
ReactDOMTextarea.restoreControlledState(domElement, props);
|
||||
ReactDOMTextareaRestoreControlledState(domElement, props);
|
||||
return;
|
||||
case 'select':
|
||||
ReactDOMSelect.restoreControlledState(domElement, props);
|
||||
ReactDOMSelectRestoreControlledState(domElement, props);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
24
packages/react-dom/src/client/ReactDOMFB.js
vendored
24
packages/react-dom/src/client/ReactDOMFB.js
vendored
@@ -7,22 +7,30 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as ReactFiberTreeReflection from 'react-reconciler/reflection';
|
||||
import * as ReactInstanceMap from 'shared/ReactInstanceMap';
|
||||
import {findCurrentFiberUsingSlowPath} from 'react-reconciler/reflection';
|
||||
import {get as getInstance} from 'shared/ReactInstanceMap';
|
||||
import {addUserTimingListener} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import ReactDOM from './ReactDOM';
|
||||
import * as ReactBrowserEventEmitter from '../events/ReactBrowserEventEmitter';
|
||||
import * as ReactDOMComponentTree from './ReactDOMComponentTree';
|
||||
import {isEnabled} from '../events/ReactBrowserEventEmitter';
|
||||
import {getClosestInstanceFromNode} from './ReactDOMComponentTree';
|
||||
|
||||
Object.assign(
|
||||
(ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: any),
|
||||
{
|
||||
// These are real internal dependencies that are trickier to remove:
|
||||
ReactBrowserEventEmitter,
|
||||
ReactFiberTreeReflection,
|
||||
ReactDOMComponentTree,
|
||||
ReactInstanceMap,
|
||||
ReactBrowserEventEmitter: {
|
||||
isEnabled,
|
||||
},
|
||||
ReactFiberTreeReflection: {
|
||||
findCurrentFiberUsingSlowPath,
|
||||
},
|
||||
ReactDOMComponentTree: {
|
||||
getClosestInstanceFromNode,
|
||||
},
|
||||
ReactInstanceMap: {
|
||||
get: getInstance,
|
||||
},
|
||||
// Perf experiment
|
||||
addUserTimingListener,
|
||||
},
|
||||
|
||||
@@ -23,10 +23,13 @@ import {
|
||||
warnForInsertedHydratedElement,
|
||||
warnForInsertedHydratedText,
|
||||
} from './ReactDOMComponent';
|
||||
import * as ReactInputSelection from './ReactInputSelection';
|
||||
import {getSelectionInformation, restoreSelection} from './ReactInputSelection';
|
||||
import setTextContent from './setTextContent';
|
||||
import {validateDOMNesting, updatedAncestorInfo} from './validateDOMNesting';
|
||||
import * as ReactBrowserEventEmitter from '../events/ReactBrowserEventEmitter';
|
||||
import {
|
||||
isEnabled as ReactBrowserEventEmitterIsEnabled,
|
||||
setEnabled as ReactBrowserEventEmitterSetEnabled,
|
||||
} from '../events/ReactBrowserEventEmitter';
|
||||
import {getChildNamespace} from '../shared/DOMNamespaces';
|
||||
import {
|
||||
ELEMENT_NODE,
|
||||
@@ -66,6 +69,10 @@ export type ChildSet = void; // Unused
|
||||
export type TimeoutHandle = TimeoutID;
|
||||
export type NoTimeout = -1;
|
||||
|
||||
import {
|
||||
unstable_scheduleCallback as scheduleDeferredCallback,
|
||||
unstable_cancelCallback as cancelDeferredCallback,
|
||||
} from 'scheduler';
|
||||
export {
|
||||
unstable_now as now,
|
||||
unstable_scheduleCallback as scheduleDeferredCallback,
|
||||
@@ -152,15 +159,15 @@ export function getPublicInstance(instance: Instance): * {
|
||||
}
|
||||
|
||||
export function prepareForCommit(containerInfo: Container): void {
|
||||
eventsEnabled = ReactBrowserEventEmitter.isEnabled();
|
||||
selectionInformation = ReactInputSelection.getSelectionInformation();
|
||||
ReactBrowserEventEmitter.setEnabled(false);
|
||||
eventsEnabled = ReactBrowserEventEmitterIsEnabled();
|
||||
selectionInformation = getSelectionInformation();
|
||||
ReactBrowserEventEmitterSetEnabled(false);
|
||||
}
|
||||
|
||||
export function resetAfterCommit(containerInfo: Container): void {
|
||||
ReactInputSelection.restoreSelection(selectionInformation);
|
||||
restoreSelection(selectionInformation);
|
||||
selectionInformation = null;
|
||||
ReactBrowserEventEmitter.setEnabled(eventsEnabled);
|
||||
ReactBrowserEventEmitterSetEnabled(eventsEnabled);
|
||||
eventsEnabled = null;
|
||||
}
|
||||
|
||||
@@ -293,6 +300,8 @@ export const scheduleTimeout =
|
||||
export const cancelTimeout =
|
||||
typeof clearTimeout === 'function' ? clearTimeout : (undefined: any);
|
||||
export const noTimeout = -1;
|
||||
export const schedulePassiveEffects = scheduleDeferredCallback;
|
||||
export const cancelPassiveEffects = cancelDeferredCallback;
|
||||
|
||||
// -------------------
|
||||
// Mutation
|
||||
|
||||
@@ -12,11 +12,11 @@ import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCur
|
||||
import invariant from 'shared/invariant';
|
||||
import warning from 'shared/warning';
|
||||
|
||||
import * as DOMPropertyOperations from './DOMPropertyOperations';
|
||||
import {setValueForProperty} from './DOMPropertyOperations';
|
||||
import {getFiberCurrentPropsFromNode} from './ReactDOMComponentTree';
|
||||
import {getToStringValue, toString} from './ToStringValue';
|
||||
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
|
||||
import * as inputValueTracking from './inputValueTracking';
|
||||
import {updateValueIfChanged} from './inputValueTracking';
|
||||
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import type {ToStringValue} from './ToStringValue';
|
||||
@@ -129,7 +129,7 @@ export function updateChecked(element: Element, props: Object) {
|
||||
const node = ((element: any): InputWithWrapperState);
|
||||
const checked = props.checked;
|
||||
if (checked != null) {
|
||||
DOMPropertyOperations.setValueForProperty(node, 'checked', checked, false);
|
||||
setValueForProperty(node, 'checked', checked, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +389,7 @@ function updateNamedCousins(rootNode, props) {
|
||||
|
||||
// We need update the tracked value on the named cousin since the value
|
||||
// was changed but the input saw no event or value set
|
||||
inputValueTracking.updateValueIfChanged(otherNode);
|
||||
updateValueIfChanged(otherNode);
|
||||
|
||||
// If this is a controlled radio button group, forcing the input that
|
||||
// was previously checked to update will cause it to be come re-checked
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import getActiveElement from './getActiveElement';
|
||||
|
||||
import * as ReactDOMSelection from './ReactDOMSelection';
|
||||
import {getOffsets, setOffsets} from './ReactDOMSelection';
|
||||
import {ELEMENT_NODE, TEXT_NODE} from '../shared/HTMLNodeType';
|
||||
|
||||
function isTextNode(node) {
|
||||
@@ -152,7 +152,7 @@ export function getSelection(input) {
|
||||
};
|
||||
} else {
|
||||
// Content editable or old IE textarea.
|
||||
selection = ReactDOMSelection.getOffsets(input);
|
||||
selection = getOffsets(input);
|
||||
}
|
||||
|
||||
return selection || {start: 0, end: 0};
|
||||
@@ -174,6 +174,6 @@ export function setSelection(input, offsets) {
|
||||
input.selectionStart = start;
|
||||
input.selectionEnd = Math.min(end, input.value.length);
|
||||
} else {
|
||||
ReactDOMSelection.setOffsets(input, offsets);
|
||||
setOffsets(input, offsets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,11 @@ import {
|
||||
TOP_TEXT_INPUT,
|
||||
TOP_PASTE,
|
||||
} from './DOMTopLevelEventTypes';
|
||||
import * as FallbackCompositionState from './FallbackCompositionState';
|
||||
import {
|
||||
getData as FallbackCompositionStateGetData,
|
||||
initialize as FallbackCompositionStateInitialize,
|
||||
reset as FallbackCompositionStateReset,
|
||||
} from './FallbackCompositionState';
|
||||
import SyntheticCompositionEvent from './SyntheticCompositionEvent';
|
||||
import SyntheticInputEvent from './SyntheticInputEvent';
|
||||
|
||||
@@ -246,10 +250,10 @@ function extractCompositionEvent(
|
||||
// The current composition is stored statically and must not be
|
||||
// overwritten while composition continues.
|
||||
if (!isComposing && eventType === eventTypes.compositionStart) {
|
||||
isComposing = FallbackCompositionState.initialize(nativeEventTarget);
|
||||
isComposing = FallbackCompositionStateInitialize(nativeEventTarget);
|
||||
} else if (eventType === eventTypes.compositionEnd) {
|
||||
if (isComposing) {
|
||||
fallbackData = FallbackCompositionState.getData();
|
||||
fallbackData = FallbackCompositionStateGetData();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,8 +350,8 @@ function getFallbackBeforeInputChars(topLevelType: TopLevelType, nativeEvent) {
|
||||
(!canUseCompositionEvent &&
|
||||
isFallbackCompositionEnd(topLevelType, nativeEvent))
|
||||
) {
|
||||
const chars = FallbackCompositionState.getData();
|
||||
FallbackCompositionState.reset();
|
||||
const chars = FallbackCompositionStateGetData();
|
||||
FallbackCompositionStateReset();
|
||||
isComposing = false;
|
||||
return chars;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as EventPluginHub from 'events/EventPluginHub';
|
||||
import {runEventsInBatch} from 'events/EventPluginHub';
|
||||
import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
|
||||
import {enqueueStateRestore} from 'events/ReactControlledComponent';
|
||||
import {batchedUpdates} from 'events/ReactGenericBatching';
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
import getEventTarget from './getEventTarget';
|
||||
import isEventSupported from './isEventSupported';
|
||||
import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
|
||||
import * as inputValueTracking from '../client/inputValueTracking';
|
||||
import {updateValueIfChanged} from '../client/inputValueTracking';
|
||||
import {setDefaultValue} from '../client/ReactDOMInput';
|
||||
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';
|
||||
|
||||
@@ -100,12 +100,12 @@ function manualDispatchChangeEvent(nativeEvent) {
|
||||
}
|
||||
|
||||
function runEventInBatch(event) {
|
||||
EventPluginHub.runEventsInBatch(event);
|
||||
runEventsInBatch(event);
|
||||
}
|
||||
|
||||
function getInstIfValueChanged(targetInst) {
|
||||
const targetNode = getNodeFromInstance(targetInst);
|
||||
if (inputValueTracking.updateValueIfChanged(targetNode)) {
|
||||
if (updateValueIfChanged(targetNode)) {
|
||||
return targetInst;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import {isListeningToAllDependencies} from './ReactBrowserEventEmitter';
|
||||
import getActiveElement from '../client/getActiveElement';
|
||||
import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
|
||||
import * as ReactInputSelection from '../client/ReactInputSelection';
|
||||
import {hasSelectionCapabilities} from '../client/ReactInputSelection';
|
||||
import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
|
||||
|
||||
const skipSelectionChangeEvent =
|
||||
@@ -66,10 +66,7 @@ let mouseDown = false;
|
||||
* @return {object}
|
||||
*/
|
||||
function getSelection(node) {
|
||||
if (
|
||||
'selectionStart' in node &&
|
||||
ReactInputSelection.hasSelectionCapabilities(node)
|
||||
) {
|
||||
if ('selectionStart' in node && hasSelectionCapabilities(node)) {
|
||||
return {
|
||||
start: node.selectionStart,
|
||||
end: node.selectionEnd,
|
||||
|
||||
898
packages/react-dom/src/fire/ReactFire.js
vendored
Normal file
898
packages/react-dom/src/fire/ReactFire.js
vendored
Normal file
@@ -0,0 +1,898 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// This file is copy paste from ReactDOM with adjusted paths
|
||||
// and a different host config import (react-reconciler/inline.fire).
|
||||
// TODO: real implementation.
|
||||
// console.log('Hello from Fire entry point.');
|
||||
|
||||
import type {ReactNodeList} from 'shared/ReactTypes';
|
||||
// TODO: This type is shared between the reconciler and ReactDOM, but will
|
||||
// eventually be lifted out to the renderer.
|
||||
import type {
|
||||
FiberRoot,
|
||||
Batch as FiberRootBatch,
|
||||
} from 'react-reconciler/src/ReactFiberRoot';
|
||||
|
||||
import '../shared/checkReact';
|
||||
import '../client/ReactDOMClientInjection';
|
||||
|
||||
import {
|
||||
computeUniqueAsyncExpiration,
|
||||
findHostInstanceWithNoPortals,
|
||||
updateContainerAtExpirationTime,
|
||||
flushRoot,
|
||||
createContainer,
|
||||
updateContainer,
|
||||
batchedUpdates,
|
||||
unbatchedUpdates,
|
||||
interactiveUpdates,
|
||||
flushInteractiveUpdates,
|
||||
flushSync,
|
||||
flushControlled,
|
||||
injectIntoDevTools,
|
||||
getPublicRootInstance,
|
||||
findHostInstance,
|
||||
findHostInstanceWithWarning,
|
||||
} from 'react-reconciler/inline.fire';
|
||||
import {createPortal as createPortalImpl} from 'shared/ReactPortal';
|
||||
import {canUseDOM} from 'shared/ExecutionEnvironment';
|
||||
import {setBatchingImplementation} from 'events/ReactGenericBatching';
|
||||
import {
|
||||
setRestoreImplementation,
|
||||
enqueueStateRestore,
|
||||
restoreStateIfNeeded,
|
||||
} from 'events/ReactControlledComponent';
|
||||
import {
|
||||
injection as EventPluginHubInjection,
|
||||
runEventsInBatch,
|
||||
} from 'events/EventPluginHub';
|
||||
import {eventNameDispatchConfigs} from 'events/EventPluginRegistry';
|
||||
import {
|
||||
accumulateTwoPhaseDispatches,
|
||||
accumulateDirectDispatches,
|
||||
} from 'events/EventPropagators';
|
||||
import {has as hasInstance} from 'shared/ReactInstanceMap';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import invariant from 'shared/invariant';
|
||||
import lowPriorityWarning from 'shared/lowPriorityWarning';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
|
||||
|
||||
import {
|
||||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
getFiberCurrentPropsFromNode,
|
||||
getClosestInstanceFromNode,
|
||||
} from '../client/ReactDOMComponentTree';
|
||||
import {restoreControlledState} from '../client/ReactDOMComponent';
|
||||
import {dispatchEvent} from '../events/ReactDOMEventListener';
|
||||
import {
|
||||
ELEMENT_NODE,
|
||||
COMMENT_NODE,
|
||||
DOCUMENT_NODE,
|
||||
DOCUMENT_FRAGMENT_NODE,
|
||||
} from '../shared/HTMLNodeType';
|
||||
import {ROOT_ATTRIBUTE_NAME} from '../shared/DOMProperty';
|
||||
|
||||
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
|
||||
let topLevelUpdateWarnings;
|
||||
let warnOnInvalidCallback;
|
||||
let didWarnAboutUnstableCreatePortal = false;
|
||||
|
||||
if (__DEV__) {
|
||||
if (
|
||||
typeof Map !== 'function' ||
|
||||
// $FlowIssue Flow incorrectly thinks Map has no prototype
|
||||
Map.prototype == null ||
|
||||
typeof Map.prototype.forEach !== 'function' ||
|
||||
typeof Set !== 'function' ||
|
||||
// $FlowIssue Flow incorrectly thinks Set has no prototype
|
||||
Set.prototype == null ||
|
||||
typeof Set.prototype.clear !== 'function' ||
|
||||
typeof Set.prototype.forEach !== 'function'
|
||||
) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'React depends on Map and Set built-in types. Make sure that you load a ' +
|
||||
'polyfill in older browsers. https://fb.me/react-polyfills',
|
||||
);
|
||||
}
|
||||
|
||||
topLevelUpdateWarnings = (container: DOMContainer) => {
|
||||
if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
|
||||
const hostInstance = findHostInstanceWithNoPortals(
|
||||
container._reactRootContainer._internalRoot.current,
|
||||
);
|
||||
if (hostInstance) {
|
||||
warningWithoutStack(
|
||||
hostInstance.parentNode === container,
|
||||
'render(...): It looks like the React-rendered content of this ' +
|
||||
'container was removed without using React. This is not ' +
|
||||
'supported and will cause errors. Instead, call ' +
|
||||
'ReactDOM.unmountComponentAtNode to empty a container.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const isRootRenderedBySomeReact = !!container._reactRootContainer;
|
||||
const rootEl = getReactRootElementInContainer(container);
|
||||
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));
|
||||
|
||||
warningWithoutStack(
|
||||
!hasNonRootReactChild || isRootRenderedBySomeReact,
|
||||
'render(...): Replacing React-rendered children with a new root ' +
|
||||
'component. If you intended to update the children of this node, ' +
|
||||
'you should instead have the existing children update their state ' +
|
||||
'and render the new components instead of calling ReactDOM.render.',
|
||||
);
|
||||
|
||||
warningWithoutStack(
|
||||
container.nodeType !== ELEMENT_NODE ||
|
||||
!((container: any): Element).tagName ||
|
||||
((container: any): Element).tagName.toUpperCase() !== 'BODY',
|
||||
'render(): Rendering components directly into document.body is ' +
|
||||
'discouraged, since its children are often manipulated by third-party ' +
|
||||
'scripts and browser extensions. This may lead to subtle ' +
|
||||
'reconciliation issues. Try rendering into a container element created ' +
|
||||
'for your app.',
|
||||
);
|
||||
};
|
||||
|
||||
warnOnInvalidCallback = function(callback: mixed, callerName: string) {
|
||||
warningWithoutStack(
|
||||
callback === null || typeof callback === 'function',
|
||||
'%s(...): Expected the last optional `callback` argument to be a ' +
|
||||
'function. Instead received: %s.',
|
||||
callerName,
|
||||
callback,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
setRestoreImplementation(restoreControlledState);
|
||||
|
||||
export type DOMContainer =
|
||||
| (Element & {
|
||||
_reactRootContainer: ?Root,
|
||||
_reactHasBeenPassedToCreateRootDEV: ?boolean,
|
||||
})
|
||||
| (Document & {
|
||||
_reactRootContainer: ?Root,
|
||||
_reactHasBeenPassedToCreateRootDEV: ?boolean,
|
||||
});
|
||||
|
||||
type Batch = FiberRootBatch & {
|
||||
render(children: ReactNodeList): Work,
|
||||
then(onComplete: () => mixed): void,
|
||||
commit(): void,
|
||||
|
||||
// The ReactRoot constructor is hoisted but the prototype methods are not. If
|
||||
// we move ReactRoot to be above ReactBatch, the inverse error occurs.
|
||||
// $FlowFixMe Hoisting issue.
|
||||
_root: Root,
|
||||
_hasChildren: boolean,
|
||||
_children: ReactNodeList,
|
||||
|
||||
_callbacks: Array<() => mixed> | null,
|
||||
_didComplete: boolean,
|
||||
};
|
||||
|
||||
type Root = {
|
||||
render(children: ReactNodeList, callback: ?() => mixed): Work,
|
||||
unmount(callback: ?() => mixed): Work,
|
||||
legacy_renderSubtreeIntoContainer(
|
||||
parentComponent: ?React$Component<any, any>,
|
||||
children: ReactNodeList,
|
||||
callback: ?() => mixed,
|
||||
): Work,
|
||||
createBatch(): Batch,
|
||||
|
||||
_internalRoot: FiberRoot,
|
||||
};
|
||||
|
||||
function ReactBatch(root: ReactRoot) {
|
||||
const expirationTime = computeUniqueAsyncExpiration();
|
||||
this._expirationTime = expirationTime;
|
||||
this._root = root;
|
||||
this._next = null;
|
||||
this._callbacks = null;
|
||||
this._didComplete = false;
|
||||
this._hasChildren = false;
|
||||
this._children = null;
|
||||
this._defer = true;
|
||||
}
|
||||
ReactBatch.prototype.render = function(children: ReactNodeList) {
|
||||
invariant(
|
||||
this._defer,
|
||||
'batch.render: Cannot render a batch that already committed.',
|
||||
);
|
||||
this._hasChildren = true;
|
||||
this._children = children;
|
||||
const internalRoot = this._root._internalRoot;
|
||||
const expirationTime = this._expirationTime;
|
||||
const work = new ReactWork();
|
||||
updateContainerAtExpirationTime(
|
||||
children,
|
||||
internalRoot,
|
||||
null,
|
||||
expirationTime,
|
||||
work._onCommit,
|
||||
);
|
||||
return work;
|
||||
};
|
||||
ReactBatch.prototype.then = function(onComplete: () => mixed) {
|
||||
if (this._didComplete) {
|
||||
onComplete();
|
||||
return;
|
||||
}
|
||||
let callbacks = this._callbacks;
|
||||
if (callbacks === null) {
|
||||
callbacks = this._callbacks = [];
|
||||
}
|
||||
callbacks.push(onComplete);
|
||||
};
|
||||
ReactBatch.prototype.commit = function() {
|
||||
const internalRoot = this._root._internalRoot;
|
||||
let firstBatch = internalRoot.firstBatch;
|
||||
invariant(
|
||||
this._defer && firstBatch !== null,
|
||||
'batch.commit: Cannot commit a batch multiple times.',
|
||||
);
|
||||
|
||||
if (!this._hasChildren) {
|
||||
// This batch is empty. Return.
|
||||
this._next = null;
|
||||
this._defer = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let expirationTime = this._expirationTime;
|
||||
|
||||
// Ensure this is the first batch in the list.
|
||||
if (firstBatch !== this) {
|
||||
// This batch is not the earliest batch. We need to move it to the front.
|
||||
// Update its expiration time to be the expiration time of the earliest
|
||||
// batch, so that we can flush it without flushing the other batches.
|
||||
if (this._hasChildren) {
|
||||
expirationTime = this._expirationTime = firstBatch._expirationTime;
|
||||
// Rendering this batch again ensures its children will be the final state
|
||||
// when we flush (updates are processed in insertion order: last
|
||||
// update wins).
|
||||
// TODO: This forces a restart. Should we print a warning?
|
||||
this.render(this._children);
|
||||
}
|
||||
|
||||
// Remove the batch from the list.
|
||||
let previous = null;
|
||||
let batch = firstBatch;
|
||||
while (batch !== this) {
|
||||
previous = batch;
|
||||
batch = batch._next;
|
||||
}
|
||||
invariant(
|
||||
previous !== null,
|
||||
'batch.commit: Cannot commit a batch multiple times.',
|
||||
);
|
||||
previous._next = batch._next;
|
||||
|
||||
// Add it to the front.
|
||||
this._next = firstBatch;
|
||||
firstBatch = internalRoot.firstBatch = this;
|
||||
}
|
||||
|
||||
// Synchronously flush all the work up to this batch's expiration time.
|
||||
this._defer = false;
|
||||
flushRoot(internalRoot, expirationTime);
|
||||
|
||||
// Pop the batch from the list.
|
||||
const next = this._next;
|
||||
this._next = null;
|
||||
firstBatch = internalRoot.firstBatch = next;
|
||||
|
||||
// Append the next earliest batch's children to the update queue.
|
||||
if (firstBatch !== null && firstBatch._hasChildren) {
|
||||
firstBatch.render(firstBatch._children);
|
||||
}
|
||||
};
|
||||
ReactBatch.prototype._onComplete = function() {
|
||||
if (this._didComplete) {
|
||||
return;
|
||||
}
|
||||
this._didComplete = true;
|
||||
const callbacks = this._callbacks;
|
||||
if (callbacks === null) {
|
||||
return;
|
||||
}
|
||||
// TODO: Error handling.
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
const callback = callbacks[i];
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
type Work = {
|
||||
then(onCommit: () => mixed): void,
|
||||
_onCommit: () => void,
|
||||
_callbacks: Array<() => mixed> | null,
|
||||
_didCommit: boolean,
|
||||
};
|
||||
|
||||
function ReactWork() {
|
||||
this._callbacks = null;
|
||||
this._didCommit = false;
|
||||
// TODO: Avoid need to bind by replacing callbacks in the update queue with
|
||||
// list of Work objects.
|
||||
this._onCommit = this._onCommit.bind(this);
|
||||
}
|
||||
ReactWork.prototype.then = function(onCommit: () => mixed): void {
|
||||
if (this._didCommit) {
|
||||
onCommit();
|
||||
return;
|
||||
}
|
||||
let callbacks = this._callbacks;
|
||||
if (callbacks === null) {
|
||||
callbacks = this._callbacks = [];
|
||||
}
|
||||
callbacks.push(onCommit);
|
||||
};
|
||||
ReactWork.prototype._onCommit = function(): void {
|
||||
if (this._didCommit) {
|
||||
return;
|
||||
}
|
||||
this._didCommit = true;
|
||||
const callbacks = this._callbacks;
|
||||
if (callbacks === null) {
|
||||
return;
|
||||
}
|
||||
// TODO: Error handling.
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
const callback = callbacks[i];
|
||||
invariant(
|
||||
typeof callback === 'function',
|
||||
'Invalid argument passed as callback. Expected a function. Instead ' +
|
||||
'received: %s',
|
||||
callback,
|
||||
);
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
function ReactRoot(
|
||||
container: DOMContainer,
|
||||
isConcurrent: boolean,
|
||||
hydrate: boolean,
|
||||
) {
|
||||
const root = createContainer(container, isConcurrent, hydrate);
|
||||
this._internalRoot = root;
|
||||
}
|
||||
ReactRoot.prototype.render = function(
|
||||
children: ReactNodeList,
|
||||
callback: ?() => mixed,
|
||||
): Work {
|
||||
const root = this._internalRoot;
|
||||
const work = new ReactWork();
|
||||
callback = callback === undefined ? null : callback;
|
||||
if (__DEV__) {
|
||||
warnOnInvalidCallback(callback, 'render');
|
||||
}
|
||||
if (callback !== null) {
|
||||
work.then(callback);
|
||||
}
|
||||
updateContainer(children, root, null, work._onCommit);
|
||||
return work;
|
||||
};
|
||||
ReactRoot.prototype.unmount = function(callback: ?() => mixed): Work {
|
||||
const root = this._internalRoot;
|
||||
const work = new ReactWork();
|
||||
callback = callback === undefined ? null : callback;
|
||||
if (__DEV__) {
|
||||
warnOnInvalidCallback(callback, 'render');
|
||||
}
|
||||
if (callback !== null) {
|
||||
work.then(callback);
|
||||
}
|
||||
updateContainer(null, root, null, work._onCommit);
|
||||
return work;
|
||||
};
|
||||
ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
|
||||
parentComponent: ?React$Component<any, any>,
|
||||
children: ReactNodeList,
|
||||
callback: ?() => mixed,
|
||||
): Work {
|
||||
const root = this._internalRoot;
|
||||
const work = new ReactWork();
|
||||
callback = callback === undefined ? null : callback;
|
||||
if (__DEV__) {
|
||||
warnOnInvalidCallback(callback, 'render');
|
||||
}
|
||||
if (callback !== null) {
|
||||
work.then(callback);
|
||||
}
|
||||
updateContainer(children, root, parentComponent, work._onCommit);
|
||||
return work;
|
||||
};
|
||||
ReactRoot.prototype.createBatch = function(): Batch {
|
||||
const batch = new ReactBatch(this);
|
||||
const expirationTime = batch._expirationTime;
|
||||
|
||||
const internalRoot = this._internalRoot;
|
||||
const firstBatch = internalRoot.firstBatch;
|
||||
if (firstBatch === null) {
|
||||
internalRoot.firstBatch = batch;
|
||||
batch._next = null;
|
||||
} else {
|
||||
// Insert sorted by expiration time then insertion order
|
||||
let insertAfter = null;
|
||||
let insertBefore = firstBatch;
|
||||
while (
|
||||
insertBefore !== null &&
|
||||
insertBefore._expirationTime >= expirationTime
|
||||
) {
|
||||
insertAfter = insertBefore;
|
||||
insertBefore = insertBefore._next;
|
||||
}
|
||||
batch._next = insertBefore;
|
||||
if (insertAfter !== null) {
|
||||
insertAfter._next = batch;
|
||||
}
|
||||
}
|
||||
|
||||
return batch;
|
||||
};
|
||||
|
||||
/**
|
||||
* True if the supplied DOM node is a valid node element.
|
||||
*
|
||||
* @param {?DOMElement} node The candidate DOM node.
|
||||
* @return {boolean} True if the DOM is a valid DOM node.
|
||||
* @internal
|
||||
*/
|
||||
function isValidContainer(node) {
|
||||
return !!(
|
||||
node &&
|
||||
(node.nodeType === ELEMENT_NODE ||
|
||||
node.nodeType === DOCUMENT_NODE ||
|
||||
node.nodeType === DOCUMENT_FRAGMENT_NODE ||
|
||||
(node.nodeType === COMMENT_NODE &&
|
||||
node.nodeValue === ' react-mount-point-unstable '))
|
||||
);
|
||||
}
|
||||
|
||||
function getReactRootElementInContainer(container: any) {
|
||||
if (!container) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (container.nodeType === DOCUMENT_NODE) {
|
||||
return container.documentElement;
|
||||
} else {
|
||||
return container.firstChild;
|
||||
}
|
||||
}
|
||||
|
||||
function shouldHydrateDueToLegacyHeuristic(container) {
|
||||
const rootElement = getReactRootElementInContainer(container);
|
||||
return !!(
|
||||
rootElement &&
|
||||
rootElement.nodeType === ELEMENT_NODE &&
|
||||
rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
|
||||
);
|
||||
}
|
||||
|
||||
setBatchingImplementation(
|
||||
batchedUpdates,
|
||||
interactiveUpdates,
|
||||
flushInteractiveUpdates,
|
||||
);
|
||||
|
||||
let warnedAboutHydrateAPI = false;
|
||||
|
||||
function legacyCreateRootFromDOMContainer(
|
||||
container: DOMContainer,
|
||||
forceHydrate: boolean,
|
||||
): Root {
|
||||
const shouldHydrate =
|
||||
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
|
||||
// First clear any existing content.
|
||||
if (!shouldHydrate) {
|
||||
let warned = false;
|
||||
let rootSibling;
|
||||
while ((rootSibling = container.lastChild)) {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
!warned &&
|
||||
rootSibling.nodeType === ELEMENT_NODE &&
|
||||
(rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME)
|
||||
) {
|
||||
warned = true;
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'render(): Target node has markup rendered by React, but there ' +
|
||||
'are unrelated nodes as well. This is most commonly caused by ' +
|
||||
'white-space inserted around server-rendered markup.',
|
||||
);
|
||||
}
|
||||
}
|
||||
container.removeChild(rootSibling);
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
|
||||
warnedAboutHydrateAPI = true;
|
||||
lowPriorityWarning(
|
||||
false,
|
||||
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
|
||||
'will stop working in React v17. Replace the ReactDOM.render() call ' +
|
||||
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
|
||||
);
|
||||
}
|
||||
}
|
||||
// Legacy roots are not async by default.
|
||||
const isConcurrent = false;
|
||||
return new ReactRoot(container, isConcurrent, shouldHydrate);
|
||||
}
|
||||
|
||||
function legacyRenderSubtreeIntoContainer(
|
||||
parentComponent: ?React$Component<any, any>,
|
||||
children: ReactNodeList,
|
||||
container: DOMContainer,
|
||||
forceHydrate: boolean,
|
||||
callback: ?Function,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
topLevelUpdateWarnings(container);
|
||||
}
|
||||
|
||||
// TODO: Without `any` type, Flow says "Property cannot be accessed on any
|
||||
// member of intersection type." Whyyyyyy.
|
||||
let root: Root = (container._reactRootContainer: any);
|
||||
if (!root) {
|
||||
// Initial mount
|
||||
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
|
||||
container,
|
||||
forceHydrate,
|
||||
);
|
||||
if (typeof callback === 'function') {
|
||||
const originalCallback = callback;
|
||||
callback = function() {
|
||||
const instance = getPublicRootInstance(root._internalRoot);
|
||||
originalCallback.call(instance);
|
||||
};
|
||||
}
|
||||
// Initial mount should not be batched.
|
||||
unbatchedUpdates(() => {
|
||||
if (parentComponent != null) {
|
||||
root.legacy_renderSubtreeIntoContainer(
|
||||
parentComponent,
|
||||
children,
|
||||
callback,
|
||||
);
|
||||
} else {
|
||||
root.render(children, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (typeof callback === 'function') {
|
||||
const originalCallback = callback;
|
||||
callback = function() {
|
||||
const instance = getPublicRootInstance(root._internalRoot);
|
||||
originalCallback.call(instance);
|
||||
};
|
||||
}
|
||||
// Update
|
||||
if (parentComponent != null) {
|
||||
root.legacy_renderSubtreeIntoContainer(
|
||||
parentComponent,
|
||||
children,
|
||||
callback,
|
||||
);
|
||||
} else {
|
||||
root.render(children, callback);
|
||||
}
|
||||
}
|
||||
return getPublicRootInstance(root._internalRoot);
|
||||
}
|
||||
|
||||
function createPortal(
|
||||
children: ReactNodeList,
|
||||
container: DOMContainer,
|
||||
key: ?string = null,
|
||||
) {
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
// TODO: pass ReactDOM portal implementation as third argument
|
||||
return createPortalImpl(children, container, null, key);
|
||||
}
|
||||
|
||||
const ReactDOM: Object = {
|
||||
createPortal,
|
||||
|
||||
findDOMNode(
|
||||
componentOrElement: Element | ?React$Component<any, any>,
|
||||
): null | Element | Text {
|
||||
if (__DEV__) {
|
||||
let owner = (ReactCurrentOwner.current: any);
|
||||
if (owner !== null && owner.stateNode !== null) {
|
||||
const warnedAboutRefsInRender =
|
||||
owner.stateNode._warnedAboutRefsInRender;
|
||||
warningWithoutStack(
|
||||
warnedAboutRefsInRender,
|
||||
'%s is accessing findDOMNode inside its render(). ' +
|
||||
'render() should be a pure function of props and state. It should ' +
|
||||
'never access something that requires stale data from the previous ' +
|
||||
'render, such as refs. Move this logic to componentDidMount and ' +
|
||||
'componentDidUpdate instead.',
|
||||
getComponentName(owner.type) || 'A component',
|
||||
);
|
||||
owner.stateNode._warnedAboutRefsInRender = true;
|
||||
}
|
||||
}
|
||||
if (componentOrElement == null) {
|
||||
return null;
|
||||
}
|
||||
if ((componentOrElement: any).nodeType === ELEMENT_NODE) {
|
||||
return (componentOrElement: any);
|
||||
}
|
||||
if (__DEV__) {
|
||||
return findHostInstanceWithWarning(componentOrElement, 'findDOMNode');
|
||||
}
|
||||
return findHostInstance(componentOrElement);
|
||||
},
|
||||
|
||||
hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactHasBeenPassedToCreateRootDEV,
|
||||
'You are calling ReactDOM.hydrate() on a container that was previously ' +
|
||||
'passed to ReactDOM.%s(). This is not supported. ' +
|
||||
'Did you mean to call root.render(element, {hydrate: true})?',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
}
|
||||
// TODO: throw or warn if we couldn't hydrate?
|
||||
return legacyRenderSubtreeIntoContainer(
|
||||
null,
|
||||
element,
|
||||
container,
|
||||
true,
|
||||
callback,
|
||||
);
|
||||
},
|
||||
|
||||
render(
|
||||
element: React$Element<any>,
|
||||
container: DOMContainer,
|
||||
callback: ?Function,
|
||||
) {
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactHasBeenPassedToCreateRootDEV,
|
||||
'You are calling ReactDOM.render() on a container that was previously ' +
|
||||
'passed to ReactDOM.%s(). This is not supported. ' +
|
||||
'Did you mean to call root.render(element)?',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
}
|
||||
return legacyRenderSubtreeIntoContainer(
|
||||
null,
|
||||
element,
|
||||
container,
|
||||
false,
|
||||
callback,
|
||||
);
|
||||
},
|
||||
|
||||
unstable_renderSubtreeIntoContainer(
|
||||
parentComponent: React$Component<any, any>,
|
||||
element: React$Element<any>,
|
||||
containerNode: DOMContainer,
|
||||
callback: ?Function,
|
||||
) {
|
||||
invariant(
|
||||
isValidContainer(containerNode),
|
||||
'Target container is not a DOM element.',
|
||||
);
|
||||
invariant(
|
||||
parentComponent != null && hasInstance(parentComponent),
|
||||
'parentComponent must be a valid React Component',
|
||||
);
|
||||
return legacyRenderSubtreeIntoContainer(
|
||||
parentComponent,
|
||||
element,
|
||||
containerNode,
|
||||
false,
|
||||
callback,
|
||||
);
|
||||
},
|
||||
|
||||
unmountComponentAtNode(container: DOMContainer) {
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'unmountComponentAtNode(...): Target container is not a DOM element.',
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactHasBeenPassedToCreateRootDEV,
|
||||
'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' +
|
||||
'passed to ReactDOM.%s(). This is not supported. Did you mean to call root.unmount()?',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
}
|
||||
|
||||
if (container._reactRootContainer) {
|
||||
if (__DEV__) {
|
||||
const rootEl = getReactRootElementInContainer(container);
|
||||
const renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl);
|
||||
warningWithoutStack(
|
||||
!renderedByDifferentReact,
|
||||
"unmountComponentAtNode(): The node you're attempting to unmount " +
|
||||
'was rendered by another copy of React.',
|
||||
);
|
||||
}
|
||||
|
||||
// Unmount should not be batched.
|
||||
unbatchedUpdates(() => {
|
||||
legacyRenderSubtreeIntoContainer(null, null, container, false, () => {
|
||||
container._reactRootContainer = null;
|
||||
});
|
||||
});
|
||||
// If you call unmountComponentAtNode twice in quick succession, you'll
|
||||
// get `true` twice. That's probably fine?
|
||||
return true;
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
const rootEl = getReactRootElementInContainer(container);
|
||||
const hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));
|
||||
|
||||
// Check if the container itself is a React root node.
|
||||
const isContainerReactRoot =
|
||||
container.nodeType === ELEMENT_NODE &&
|
||||
isValidContainer(container.parentNode) &&
|
||||
!!container.parentNode._reactRootContainer;
|
||||
|
||||
warningWithoutStack(
|
||||
!hasNonRootReactChild,
|
||||
"unmountComponentAtNode(): The node you're attempting to unmount " +
|
||||
'was rendered by React and is not a top-level container. %s',
|
||||
isContainerReactRoot
|
||||
? 'You may have accidentally passed in a React root node instead ' +
|
||||
'of its container.'
|
||||
: 'Instead, have the parent component update its state and ' +
|
||||
'rerender in order to remove this component.',
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// Temporary alias since we already shipped React 16 RC with it.
|
||||
// TODO: remove in React 17.
|
||||
unstable_createPortal(...args) {
|
||||
if (!didWarnAboutUnstableCreatePortal) {
|
||||
didWarnAboutUnstableCreatePortal = true;
|
||||
lowPriorityWarning(
|
||||
false,
|
||||
'The ReactDOM.unstable_createPortal() alias has been deprecated, ' +
|
||||
'and will be removed in React 17+. Update your code to use ' +
|
||||
'ReactDOM.createPortal() instead. It has the exact same API, ' +
|
||||
'but without the "unstable_" prefix.',
|
||||
);
|
||||
}
|
||||
return createPortal(...args);
|
||||
},
|
||||
|
||||
unstable_batchedUpdates: batchedUpdates,
|
||||
|
||||
unstable_interactiveUpdates: interactiveUpdates,
|
||||
|
||||
flushSync: flushSync,
|
||||
|
||||
unstable_createRoot: createRoot,
|
||||
unstable_flushControlled: flushControlled,
|
||||
|
||||
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
|
||||
// Keep in sync with ReactDOMUnstableNativeDependencies.js
|
||||
// and ReactTestUtils.js. This is an array for better minification.
|
||||
Events: [
|
||||
getInstanceFromNode,
|
||||
getNodeFromInstance,
|
||||
getFiberCurrentPropsFromNode,
|
||||
EventPluginHubInjection.injectEventPluginsByName,
|
||||
eventNameDispatchConfigs,
|
||||
accumulateTwoPhaseDispatches,
|
||||
accumulateDirectDispatches,
|
||||
enqueueStateRestore,
|
||||
restoreStateIfNeeded,
|
||||
dispatchEvent,
|
||||
runEventsInBatch,
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
type RootOptions = {
|
||||
hydrate?: boolean,
|
||||
};
|
||||
|
||||
function createRoot(container: DOMContainer, options?: RootOptions): ReactRoot {
|
||||
const functionName = enableStableConcurrentModeAPIs
|
||||
? 'createRoot'
|
||||
: 'unstable_createRoot';
|
||||
invariant(
|
||||
isValidContainer(container),
|
||||
'%s(...): Target container is not a DOM element.',
|
||||
functionName,
|
||||
);
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!container._reactRootContainer,
|
||||
'You are calling ReactDOM.%s() on a container that was previously ' +
|
||||
'passed to ReactDOM.render(). This is not supported.',
|
||||
enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot',
|
||||
);
|
||||
container._reactHasBeenPassedToCreateRootDEV = true;
|
||||
}
|
||||
const hydrate = options != null && options.hydrate === true;
|
||||
return new ReactRoot(container, true, hydrate);
|
||||
}
|
||||
|
||||
if (enableStableConcurrentModeAPIs) {
|
||||
ReactDOM.createRoot = createRoot;
|
||||
ReactDOM.unstable_createRoot = undefined;
|
||||
}
|
||||
|
||||
const foundDevTools = injectIntoDevTools({
|
||||
findFiberByHostInstance: getClosestInstanceFromNode,
|
||||
bundleType: __DEV__ ? 1 : 0,
|
||||
version: ReactVersion,
|
||||
rendererPackageName: 'react-dom',
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
if (!foundDevTools && canUseDOM && window.top === window.self) {
|
||||
// If we're in Chrome or Firefox, provide a download link if not installed.
|
||||
if (
|
||||
(navigator.userAgent.indexOf('Chrome') > -1 &&
|
||||
navigator.userAgent.indexOf('Edge') === -1) ||
|
||||
navigator.userAgent.indexOf('Firefox') > -1
|
||||
) {
|
||||
const protocol = window.location.protocol;
|
||||
// Don't warn in exotic cases like chrome-extension://.
|
||||
if (/^(https?|file):$/.test(protocol)) {
|
||||
console.info(
|
||||
'%cDownload the React DevTools ' +
|
||||
'for a better development experience: ' +
|
||||
'https://fb.me/react-devtools' +
|
||||
(protocol === 'file:'
|
||||
? '\nYou might need to use a local HTTP server (instead of file://): ' +
|
||||
'https://fb.me/react-devtools-faq'
|
||||
: ''),
|
||||
'font-weight:bold',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ReactDOM;
|
||||
14
packages/react-dom/src/fire/ReactFireHostConfig.js
vendored
Normal file
14
packages/react-dom/src/fire/ReactFireHostConfig.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
// This is just to get the setup running.
|
||||
// TODO: real implementation.
|
||||
// console.log('Hello from Fire host config.');
|
||||
|
||||
export * from '../client/ReactDOMHostConfig';
|
||||
34
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
vendored
Normal file
34
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {ReactNodeList} from 'shared/ReactTypes';
|
||||
|
||||
import {
|
||||
createRequest,
|
||||
startWork,
|
||||
startFlowing,
|
||||
} from 'react-stream/inline.dom-browser';
|
||||
|
||||
function renderToReadableStream(children: ReactNodeList): ReadableStream {
|
||||
let request;
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
request = createRequest(children, controller);
|
||||
startWork(request);
|
||||
},
|
||||
pull(controller) {
|
||||
startFlowing(request, controller.desiredSize);
|
||||
},
|
||||
cancel(reason) {},
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
renderToReadableStream,
|
||||
};
|
||||
19
packages/react-dom/src/server/ReactDOMFizzServerFormatConfig.js
vendored
Normal file
19
packages/react-dom/src/server/ReactDOMFizzServerFormatConfig.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import {convertStringToBuffer} from 'react-stream/src/ReactFizzHostConfig';
|
||||
|
||||
export function formatChunk(type: string, props: Object): Uint8Array {
|
||||
let str = '<' + type + '>';
|
||||
if (typeof props.children === 'string') {
|
||||
str += props.children;
|
||||
}
|
||||
str += '</' + type + '>';
|
||||
return convertStringToBuffer(str);
|
||||
}
|
||||
30
packages/react-dom/src/server/ReactDOMFizzServerNode.js
vendored
Normal file
30
packages/react-dom/src/server/ReactDOMFizzServerNode.js
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {ReactNodeList} from 'shared/ReactTypes';
|
||||
import type {Writable} from 'stream';
|
||||
|
||||
import {createRequest, startWork, startFlowing} from 'react-stream/inline.dom';
|
||||
|
||||
function createDrainHandler(destination, request) {
|
||||
return () => startFlowing(request, 0);
|
||||
}
|
||||
|
||||
function pipeToNodeWritable(
|
||||
children: ReactNodeList,
|
||||
destination: Writable,
|
||||
): void {
|
||||
let request = createRequest(children, destination);
|
||||
destination.on('drain', createDrainHandler(destination, request));
|
||||
startWork(request);
|
||||
}
|
||||
|
||||
export default {
|
||||
pipeToNodeWritable,
|
||||
};
|
||||
@@ -18,8 +18,9 @@ class ReactMarkupReadableStream extends Readable {
|
||||
this.partialRenderer = new ReactPartialRenderer(element, makeStaticMarkup);
|
||||
}
|
||||
|
||||
_destroy() {
|
||||
_destroy(err, callback) {
|
||||
this.partialRenderer.destroy();
|
||||
callback(err);
|
||||
}
|
||||
|
||||
_read(size) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import describeComponentFrame from 'shared/describeComponentFrame';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import {
|
||||
warnAboutDeprecatedLifecycles,
|
||||
enableHooks,
|
||||
enableSuspenseServerRenderer,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
|
||||
@@ -55,7 +54,6 @@ import {
|
||||
prepareToUseHooks,
|
||||
finishHooks,
|
||||
Dispatcher,
|
||||
DispatcherWithoutHooks,
|
||||
currentThreadID,
|
||||
setCurrentThreadID,
|
||||
} from './ReactPartialRendererHooks';
|
||||
@@ -87,7 +85,7 @@ const toArray = ((React.Children.toArray: any): toArrayType);
|
||||
// Each stack is an array of frames which may contain nested stacks of elements.
|
||||
let currentDebugStacks = [];
|
||||
|
||||
let ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||
let ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
||||
let ReactDebugCurrentFrame;
|
||||
let prevGetCurrentStackImpl = null;
|
||||
let getCurrentServerStackImpl = () => '';
|
||||
@@ -785,12 +783,8 @@ class ReactDOMServerRenderer {
|
||||
|
||||
const prevThreadID = currentThreadID;
|
||||
setCurrentThreadID(this.threadID);
|
||||
const prevDispatcher = ReactCurrentOwner.currentDispatcher;
|
||||
if (enableHooks) {
|
||||
ReactCurrentOwner.currentDispatcher = Dispatcher;
|
||||
} else {
|
||||
ReactCurrentOwner.currentDispatcher = DispatcherWithoutHooks;
|
||||
}
|
||||
const prevDispatcher = ReactCurrentDispatcher.current;
|
||||
ReactCurrentDispatcher.current = Dispatcher;
|
||||
try {
|
||||
// Markup generated within <Suspense> ends up buffered until we know
|
||||
// nothing in that boundary suspended
|
||||
@@ -870,7 +864,7 @@ class ReactDOMServerRenderer {
|
||||
}
|
||||
return out[0];
|
||||
} finally {
|
||||
ReactCurrentOwner.currentDispatcher = prevDispatcher;
|
||||
ReactCurrentDispatcher.current = prevDispatcher;
|
||||
setCurrentThreadID(prevThreadID);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user