mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Initial support for v15+ via new legaacy renderer
This commit is contained in:
+1
-2
@@ -12,5 +12,4 @@ yarn-error.log
|
||||
.vscode
|
||||
.idea
|
||||
.watchmanconfig
|
||||
key.pem
|
||||
|
||||
key.pem
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 14.9</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@0.14.9/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@0.14.9/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/14.9.html">http://localhost:9000/fixtures/regression/14.9.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 15.0</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@15.0/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@15.0/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/15.0.html">http://localhost:9000/fixtures/regression/15.0.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 15.1</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@15.1/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@15.1/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/15.1.html">http://localhost:9000/fixtures/regression/15.1.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 15.2</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@15.2/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@15.2/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/15.2.html">http://localhost:9000/fixtures/regression/15.2.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 15.3</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@15.3/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@15.3/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/15.3.html">http://localhost:9000/fixtures/regression/15.3.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 15.4</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@15.4/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@15.4/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/15.4.html">http://localhost:9000/fixtures/regression/15.4.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 15.5</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@15.5/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@15.5/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/15.5.html">http://localhost:9000/fixtures/regression/15.5.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 15.6</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@15.6/dist/react.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@15.6/dist/react-dom.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/15.6.html">http://localhost:9000/fixtures/regression/15.6.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.0</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@16.0/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.0/umd/react-dom.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.0.html">http://localhost:9000/fixtures/regression/16.0.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.1</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@16.1/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.1/umd/react-dom.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.1.html">http://localhost:9000/fixtures/regression/16.1.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.2</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@16.2/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.2/umd/react-dom.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.2.html">http://localhost:9000/fixtures/regression/16.2.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.3</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@16.3/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.3/umd/react-dom.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.3.html">http://localhost:9000/fixtures/regression/16.3.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.4</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/react@16.4/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.4/umd/react-dom.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.4.html">http://localhost:9000/fixtures/regression/16.4.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.5</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/schedule@0.5.0/umd/schedule.development.js"></script>
|
||||
<script src="https://unpkg.com/schedule@0.5.0/umd/schedule-tracing.development.js"></script>
|
||||
<script src="https://unpkg.com/react@16.5/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.5/umd/react-dom.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.5.html">http://localhost:9000/fixtures/regression/16.5.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.6</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/scheduler@0.10.0/umd/scheduler.development.js"></script>
|
||||
<script src="https://unpkg.com/scheduler@0.10.0/umd/scheduler-tracing.development.js"></script>
|
||||
<script src="https://unpkg.com/react@16.6/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.6/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/react-cache@2.0.0-alpha.1/umd/react-cache.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.6.html">http://localhost:9000/fixtures/regression/16.6.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React 16.7</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/scheduler@0.12.0/umd/scheduler.development.js"></script>
|
||||
<script src="https://unpkg.com/scheduler@0.12.0/umd/scheduler-tracing.development.js"></script>
|
||||
<script src="https://unpkg.com/react@16.7/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@16.7/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/react-cache@2.0.0-alpha.1/umd/react-cache.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/16.7.html">http://localhost:9000/fixtures/regression/16.7.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React canary</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/scheduler@canary/umd/scheduler.development.js"></script>
|
||||
<script src="https://unpkg.com/scheduler@canary/umd/scheduler-tracing.development.js"></script>
|
||||
<script src="https://unpkg.com/react@canary/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@canary/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/react-cache@next/umd/react-cache.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/canary.html">http://localhost:9000/fixtures/regression/canary.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React DevTools regression test</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="canary.html"></iframe>
|
||||
<iframe src="next.html"></iframe>
|
||||
<iframe src="16.7.html"></iframe>
|
||||
<iframe src="16.6.html"></iframe>
|
||||
<iframe src="16.5.html"></iframe>
|
||||
<iframe src="16.4.html"></iframe>
|
||||
<iframe src="16.3.html"></iframe>
|
||||
<iframe src="16.2.html"></iframe>
|
||||
<iframe src="16.1.html"></iframe>
|
||||
<iframe src="16.0.html"></iframe>
|
||||
<iframe src="15.6.html"></iframe>
|
||||
<iframe src="15.5.html"></iframe>
|
||||
<iframe src="15.4.html"></iframe>
|
||||
<iframe src="15.3.html"></iframe>
|
||||
<iframe src="15.2.html"></iframe>
|
||||
<iframe src="15.1.html"></iframe>
|
||||
<iframe src="15.0.html"></iframe>
|
||||
<iframe src="14.9.html"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>React next</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Enable DevTools to inspect React inside of an <iframe>
|
||||
// This must run before React is loaded
|
||||
__REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/scheduler@next/umd/scheduler.development.js"></script>
|
||||
<script src="https://unpkg.com/scheduler@next/umd/scheduler-tracing.development.js"></script>
|
||||
<script src="https://unpkg.com/react@next/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@next/umd/react-dom.development.js"></script>
|
||||
<script src="https://unpkg.com/react-cache@next/umd/react-cache.development.js"></script>
|
||||
|
||||
<!-- Don't use this in production: -->
|
||||
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
If you are seeing this message, you are likely viewing this file using the <code>file</code> protocol which does not support cross origin requests.
|
||||
<br/><br/>
|
||||
Use a web server like <code>serve</code> instead:
|
||||
<br/><br/>
|
||||
<code>npm install -g pushstate-server</code><br/>
|
||||
<code>pushstate-server .</code><br/>
|
||||
<code>open <a href="http://localhost:9000/fixtures/regression/next.html">http://localhost:9000/fixtures/regression/next.html</a></code>
|
||||
</div>
|
||||
|
||||
<script src="shared.js" type="text/babel"></script>
|
||||
|
||||
<!--
|
||||
This is a great way to try React but it's not suitable for production.
|
||||
It slowly compiles JSX with Babel in the browser and uses a large development build of React.
|
||||
Learn more at https://reactjs.org/docs/getting-started.html
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,330 @@
|
||||
/* eslint-disable no-fallthrough, react/react-in-jsx-scope, react/jsx-no-undef */
|
||||
/* global React ReactCache ReactDOM SchedulerTracing ScheduleTracing */
|
||||
|
||||
const apps = [];
|
||||
|
||||
const pieces = React.version.split('.');
|
||||
const major =
|
||||
pieces[0] === '0' ? parseInt(pieces[1], 10) : parseInt(pieces[0], 10);
|
||||
const minor =
|
||||
pieces[0] === '0' ? parseInt(pieces[2], 10) : parseInt(pieces[1], 10);
|
||||
|
||||
// Convenience wrapper to organize API features in DevTools.
|
||||
function Feature({ children, label, version }) {
|
||||
return (
|
||||
<div className="Feature">
|
||||
<div className="FeatureHeader">
|
||||
<code className="FeatureCode">{label}</code>
|
||||
<small>{version}</small>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Simplify interaction tracing for tests below.
|
||||
let trace = null;
|
||||
if (typeof SchedulerTracing !== 'undefined') {
|
||||
trace = SchedulerTracing.unstable_trace;
|
||||
} else if (typeof ScheduleTracing !== 'undefined') {
|
||||
trace = ScheduleTracing.unstable_trace;
|
||||
} else {
|
||||
trace = (_, __, callback) => callback();
|
||||
}
|
||||
|
||||
// https://github.com/facebook/react/blob/master/CHANGELOG.md
|
||||
switch (major) {
|
||||
case 16:
|
||||
switch (minor) {
|
||||
case 7:
|
||||
if (typeof React.useState === 'function') {
|
||||
// Hooks
|
||||
function Hooks() {
|
||||
const [count, setCount] = React.useState(0);
|
||||
const incrementCount = React.useCallback(
|
||||
() => setCount(count + 1),
|
||||
[count]
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
count: {count}{' '}
|
||||
<button onClick={incrementCount}>increment</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
apps.push(
|
||||
<Feature key="Hooks" label="Hooks" version="16.7+">
|
||||
<Hooks />
|
||||
</Feature>
|
||||
);
|
||||
}
|
||||
case 6:
|
||||
// memo
|
||||
function LabelComponent({ label }) {
|
||||
return <label>{label}</label>;
|
||||
}
|
||||
const AnonymousMemoized = React.memo(({ label }) => (
|
||||
<label>{label}</label>
|
||||
));
|
||||
const Memoized = React.memo(LabelComponent);
|
||||
const CustomMemoized = React.memo(LabelComponent);
|
||||
CustomMemoized.displayName = 'MemoizedLabelFunction';
|
||||
apps.push(
|
||||
<Feature key="memo" label="memo" version="16.6+">
|
||||
<AnonymousMemoized label="AnonymousMemoized" />
|
||||
<Memoized label="Memoized" />
|
||||
<CustomMemoized label="CustomMemoized" />
|
||||
</Feature>
|
||||
);
|
||||
|
||||
// Suspense
|
||||
const loadResource = ([text, ms]) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
resolve(text);
|
||||
}, ms);
|
||||
});
|
||||
};
|
||||
const getResourceKey = ([text, ms]) => text;
|
||||
const Resource = ReactCache.unstable_createResource(
|
||||
loadResource,
|
||||
getResourceKey
|
||||
);
|
||||
class Suspending extends React.Component {
|
||||
state = { useSuspense: false };
|
||||
useSuspense = () => this.setState({ useSuspense: true });
|
||||
render() {
|
||||
if (this.state.useSuspense) {
|
||||
const text = Resource.read(['loaded', 2000]);
|
||||
return text;
|
||||
} else {
|
||||
return <button onClick={this.useSuspense}>load data</button>;
|
||||
}
|
||||
}
|
||||
}
|
||||
apps.push(
|
||||
<Feature key="Suspense" label="Suspense" version="16.6+">
|
||||
<React.Suspense fallback={<div>loading...</div>}>
|
||||
<Suspending />
|
||||
</React.Suspense>
|
||||
</Feature>
|
||||
);
|
||||
|
||||
// lazy
|
||||
const LazyWithDefaultProps = React.lazy(
|
||||
() =>
|
||||
new Promise(resolve => {
|
||||
function FooWithDefaultProps(props) {
|
||||
return (
|
||||
<h1>
|
||||
{props.greeting}, {props.name}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
FooWithDefaultProps.defaultProps = {
|
||||
name: 'World',
|
||||
greeting: 'Bonjour',
|
||||
};
|
||||
resolve({
|
||||
default: FooWithDefaultProps,
|
||||
});
|
||||
})
|
||||
);
|
||||
apps.push(
|
||||
<Feature key="lazy" label="lazy" version="16.6+">
|
||||
<React.Suspense fallback={<div>loading...</div>}>
|
||||
<LazyWithDefaultProps greeting="Hello" />
|
||||
</React.Suspense>
|
||||
</Feature>
|
||||
);
|
||||
case 5:
|
||||
case 4:
|
||||
// unstable_Profiler
|
||||
class ProfilerChild extends React.Component {
|
||||
state = { count: 0 };
|
||||
incrementCount = () =>
|
||||
this.setState(prevState => ({ count: prevState.count + 1 }));
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
count: {this.state.count}{' '}
|
||||
<button onClick={this.incrementCount}>increment</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
const onRender = (...args) => console.log('onRender()', ...args);
|
||||
const Profiler = React.unstable_Profiler || React.Profiler;
|
||||
apps.push(
|
||||
<Feature
|
||||
key="unstable_Profiler"
|
||||
label="unstable_Profiler"
|
||||
version="16.4+"
|
||||
>
|
||||
<Profiler id="count" onRender={onRender}>
|
||||
<div>
|
||||
<ProfilerChild />
|
||||
</div>
|
||||
</Profiler>
|
||||
</Feature>
|
||||
);
|
||||
case 3:
|
||||
// createContext()
|
||||
const LocaleContext = React.createContext();
|
||||
LocaleContext.displayName = 'LocaleContext';
|
||||
const ThemeContext = React.createContext();
|
||||
apps.push(
|
||||
<Feature key="createContext" label="createContext" version="16.3+">
|
||||
<ThemeContext.Provider value="blue">
|
||||
<ThemeContext.Consumer>
|
||||
{theme => <div>theme: {theme}</div>}
|
||||
</ThemeContext.Consumer>
|
||||
</ThemeContext.Provider>
|
||||
<LocaleContext.Provider value="en-US">
|
||||
<LocaleContext.Consumer>
|
||||
{locale => <div>locale: {locale}</div>}
|
||||
</LocaleContext.Consumer>
|
||||
</LocaleContext.Provider>
|
||||
</Feature>
|
||||
);
|
||||
|
||||
// forwardRef()
|
||||
const AnonymousFunction = React.forwardRef((props, ref) => (
|
||||
<div ref={ref}>{props.children}</div>
|
||||
));
|
||||
const NamedFunction = React.forwardRef(function named(props, ref) {
|
||||
return <div ref={ref}>{props.children}</div>;
|
||||
});
|
||||
const CustomName = React.forwardRef((props, ref) => (
|
||||
<div ref={ref}>{props.children}</div>
|
||||
));
|
||||
CustomName.displayName = 'CustomNameForwardRef';
|
||||
apps.push(
|
||||
<Feature key="forwardRef" label="forwardRef" version="16.3+">
|
||||
<AnonymousFunction>AnonymousFunction</AnonymousFunction>
|
||||
<NamedFunction>NamedFunction</NamedFunction>
|
||||
<CustomName>CustomName</CustomName>
|
||||
</Feature>
|
||||
);
|
||||
|
||||
// StrictMode
|
||||
class StrictModeChild extends React.Component {
|
||||
render() {
|
||||
return 'StrictModeChild';
|
||||
}
|
||||
}
|
||||
apps.push(
|
||||
<Feature key="StrictMode" label="StrictMode" version="16.3+">
|
||||
<React.StrictMode>
|
||||
<StrictModeChild />
|
||||
</React.StrictMode>
|
||||
</Feature>
|
||||
);
|
||||
|
||||
// unstable_AsyncMode (later renamed to unstable_ConcurrentMode, then ConcurrentMode)
|
||||
const ConcurrentMode =
|
||||
React.ConcurrentMode ||
|
||||
React.unstable_ConcurrentMode ||
|
||||
React.unstable_AsyncMode;
|
||||
apps.push(
|
||||
<Feature
|
||||
key="AsyncMode/ConcurrentMode"
|
||||
label="AsyncMode/ConcurrentMode"
|
||||
version="16.3+"
|
||||
>
|
||||
<ConcurrentMode>
|
||||
<div>
|
||||
unstable_AsyncMode was added in 16.3, renamed to
|
||||
unstable_ConcurrentMode in 16.5, and then renamed to
|
||||
ConcurrentMode in 16.7
|
||||
</div>
|
||||
</ConcurrentMode>
|
||||
</Feature>
|
||||
);
|
||||
case 2:
|
||||
// Fragment
|
||||
apps.push(
|
||||
<Feature key="Fragment" label="Fragment" version="16.4+">
|
||||
<React.Fragment>
|
||||
<div>one</div>
|
||||
<div>two</div>
|
||||
</React.Fragment>
|
||||
</Feature>
|
||||
);
|
||||
case 1:
|
||||
case 0:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 15:
|
||||
break;
|
||||
case 14:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
function Even() {
|
||||
return <small>(even)</small>;
|
||||
}
|
||||
|
||||
// Simple stateful app shared by all React versions
|
||||
class SimpleApp extends React.Component {
|
||||
state = { count: 0 };
|
||||
incrementCount = () => {
|
||||
const updaterFn = prevState => ({ count: prevState.count + 1 });
|
||||
trace('Updating count', performance.now(), () => this.setState(updaterFn));
|
||||
};
|
||||
render() {
|
||||
const { count } = this.state;
|
||||
return (
|
||||
<div>
|
||||
{count % 2 === 0 ? (
|
||||
<span>
|
||||
count: {count} <Even />
|
||||
</span>
|
||||
) : (
|
||||
<span>count: {count}</span>
|
||||
)}{' '}
|
||||
<button onClick={this.incrementCount}>increment</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
apps.push(
|
||||
<Feature key="Simple stateful app" label="Simple stateful app" version="any">
|
||||
<SimpleApp />
|
||||
</Feature>
|
||||
);
|
||||
|
||||
// This component, with the version prop, helps organize DevTools at a glance.
|
||||
function TopLevelWrapperForDevTools({ version }) {
|
||||
let header = <h1>React {version}</h1>;
|
||||
if (version.includes('canary')) {
|
||||
const commitSha = version.match(/.+canary-(.+)/)[1];
|
||||
header = (
|
||||
<h1>
|
||||
React canary{' '}
|
||||
<a href={`https://github.com/facebook/react/commit/${commitSha}`}>
|
||||
{commitSha}
|
||||
</a>
|
||||
</h1>
|
||||
);
|
||||
} else if (version.includes('alpha')) {
|
||||
header = <h1>React next</h1>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{header}
|
||||
{apps}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
TopLevelWrapperForDevTools.displayName = 'React';
|
||||
|
||||
ReactDOM.render(
|
||||
<TopLevelWrapperForDevTools version={React.version} />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
@@ -0,0 +1,37 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 1rem 0 0;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.Feature {
|
||||
margin: 1rem 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.FeatureHeader {
|
||||
font-size: 16px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.FeatureCode {
|
||||
background-color: #eee;
|
||||
padding: 0.25rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ export default class Agent extends EventEmitter {
|
||||
const renderer = ((this._rendererInterfaces[
|
||||
(rendererID: any)
|
||||
]: any): RendererInterface);
|
||||
return renderer.getFiberIDFromNative(node, true);
|
||||
return renderer.getInternalIDFromNative(node, true);
|
||||
} catch (e) {}
|
||||
}
|
||||
return null;
|
||||
@@ -253,7 +253,7 @@ export default class Agent extends EventEmitter {
|
||||
|
||||
let node: HTMLElement | null = null;
|
||||
if (renderer !== null) {
|
||||
node = ((renderer.findNativeByFiberID(id): any): HTMLElement);
|
||||
node = ((renderer.getNativeFromInternal(id): any): HTMLElement);
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { DevToolsHook, ReactRenderer, RendererInterface } from './types';
|
||||
import Agent from './agent';
|
||||
|
||||
import { attach } from './renderer';
|
||||
import { attach as attachLegacy } from './legacy/renderer';
|
||||
|
||||
export function initBackend(
|
||||
hook: DevToolsHook,
|
||||
@@ -40,7 +41,11 @@ export function initBackend(
|
||||
|
||||
// Inject any not-yet-injected renderers (if we didn't reload-and-profile)
|
||||
if (!rendererInterface) {
|
||||
rendererInterface = attach(hook, id, renderer, global);
|
||||
if (typeof renderer.findFiberByHostInstance === 'function') {
|
||||
rendererInterface = attach(hook, id, renderer, global);
|
||||
} else {
|
||||
rendererInterface = attachLegacy(hook, id, renderer, global);
|
||||
}
|
||||
|
||||
hook.rendererInterfaces.set(id, rendererInterface);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// @flow
|
||||
|
||||
export default function getChildren(internalInstance: Object): Array<any> {
|
||||
// If the parent is a native node without rendered children, but with
|
||||
// multiple string children, then the `element` that gets passed in here is
|
||||
// a plain value -- a string or number.
|
||||
if (internalInstance._renderedComponent) {
|
||||
return [internalInstance._renderedComponent];
|
||||
} else if (internalInstance._renderedChildren) {
|
||||
return childrenToArray(internalInstance._renderedChildren);
|
||||
} else if (
|
||||
internalInstance._currentElement &&
|
||||
internalInstance._currentElement.props
|
||||
) {
|
||||
// DevTools doesn't need to display primative child types,
|
||||
// So we can filter them out early.
|
||||
/*
|
||||
const children = [];
|
||||
|
||||
// This is a native node without rendered children -- meaning the children
|
||||
// prop is the unfiltered list of children.
|
||||
// This may include 'null' or even other invalid values, so we need to
|
||||
// filter it the same way that ReactDOM does.
|
||||
// Instead of pulling in the whole React library, we just copied over the
|
||||
// 'traverseAllChildrenImpl' method.
|
||||
// https://github.com/facebook/react/blob/240b84ed8e1db715d759afaae85033718a0b24e1/src/isomorphic/children/ReactChildren.js#L112-L158
|
||||
const unfilteredChildren = internalInstance._currentElement.props.children;
|
||||
traverseAllChildrenImpl(
|
||||
unfilteredChildren,
|
||||
'', // nameSoFar
|
||||
(_traverseContext, child) => {
|
||||
const childType = typeof child;
|
||||
if (childType === 'string' || childType === 'number') {
|
||||
children.push(child);
|
||||
}
|
||||
}
|
||||
// traverseContext
|
||||
);
|
||||
|
||||
return children;
|
||||
*/
|
||||
}
|
||||
|
||||
if (internalInstance._instance) {
|
||||
var inst = internalInstance._instance;
|
||||
// TODO: React ART currently falls in this bucket, but this doesn't
|
||||
// actually make sense and we should clean this up after stabilizing our
|
||||
// API for backends
|
||||
if (inst._renderedChildren) {
|
||||
return childrenToArray(inst._renderedChildren);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function childrenToArray(children) {
|
||||
const array = [];
|
||||
for (var name in children) {
|
||||
array.push(children[name]);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
ElementTypeClass,
|
||||
ElementTypeOtherOrUnknown,
|
||||
} from 'src/devtools/types';
|
||||
import { getDisplayName } from 'src/utils';
|
||||
|
||||
import type { InternalInstance } from './renderer';
|
||||
import type { FiberData } from '../types';
|
||||
|
||||
export default function getData(internalInstance: InternalInstance): FiberData {
|
||||
let displayName = null;
|
||||
let key = null;
|
||||
let type = ElementTypeOtherOrUnknown;
|
||||
|
||||
// != used deliberately here to catch undefined and null
|
||||
if (internalInstance._currentElement != null) {
|
||||
if (internalInstance._currentElement.key) {
|
||||
key = String(internalInstance._currentElement.key);
|
||||
}
|
||||
|
||||
const elementType = internalInstance._currentElement.type;
|
||||
if (typeof elementType === 'string') {
|
||||
// ...
|
||||
} else if (typeof elementType === 'function') {
|
||||
// TODO Can we differentiate between function and class component types?
|
||||
// Dan said _compositeType tells you PureClass, ImpureClass, StatelessFunctional but it was only added in v14
|
||||
// getPublicInstance() returns null for function components
|
||||
type = ElementTypeClass;
|
||||
displayName = getDisplayName(elementType);
|
||||
} else if (typeof internalInstance._stringText === 'string') {
|
||||
// ...
|
||||
} else {
|
||||
// TODO What kind of case does this cover?
|
||||
console.log('what is this type?');
|
||||
displayName = getDisplayName(elementType);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
displayName,
|
||||
key,
|
||||
type,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// @flow
|
||||
import {
|
||||
ElementTypeClass,
|
||||
ElementTypeOtherOrUnknown,
|
||||
} from 'src/devtools/types';
|
||||
|
||||
import type { InternalInstance } from './renderer';
|
||||
import type { ElementType } from 'src/devtools/types';
|
||||
|
||||
export default function getElementType(
|
||||
internalInstance: InternalInstance
|
||||
): ElementType {
|
||||
// != used deliberately here to catch undefined and null
|
||||
if (internalInstance._currentElement != null) {
|
||||
const elementType = internalInstance._currentElement.type;
|
||||
if (typeof elementType === 'function') {
|
||||
return ElementTypeClass;
|
||||
}
|
||||
}
|
||||
|
||||
return ElementTypeOtherOrUnknown;
|
||||
}
|
||||
@@ -0,0 +1,787 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
ElementTypeClass,
|
||||
ElementTypeFunction,
|
||||
ElementTypeRoot,
|
||||
ElementTypeOtherOrUnknown,
|
||||
} from 'src/devtools/types';
|
||||
import { getUID, utfEncodeString, operationsArrayToString } from '../../utils';
|
||||
import { cleanForBridge, copyWithSet } from '../utils';
|
||||
import {
|
||||
__DEBUG__,
|
||||
TREE_OPERATION_ADD,
|
||||
TREE_OPERATION_REMOVE,
|
||||
} from '../../constants';
|
||||
import getChildren from './getChildren';
|
||||
import getData from './getData';
|
||||
import getElementType from './getElementType';
|
||||
import {
|
||||
decorateResult,
|
||||
decorateMany,
|
||||
forceUpdate,
|
||||
restoreMany,
|
||||
} from './utils';
|
||||
|
||||
import type {
|
||||
DevToolsHook,
|
||||
GetInternalIDFromNative,
|
||||
GetNativeFromInternal,
|
||||
NativeType,
|
||||
RendererInterface,
|
||||
} from '../types';
|
||||
import type { InspectedElement } from 'src/devtools/views/Components/types';
|
||||
|
||||
export type InternalInstance = Object;
|
||||
type LegacyRenderer = Object;
|
||||
|
||||
export function attach(
|
||||
hook: DevToolsHook,
|
||||
rendererID: number,
|
||||
renderer: LegacyRenderer,
|
||||
global: Object
|
||||
): RendererInterface {
|
||||
const idToInternalInstanceMap: Map<number, InternalInstance> = new Map();
|
||||
const idToParentIDMap: Map<number, number> = new Map();
|
||||
const internalInstanceToIDMap: Map<InternalInstance, number> = new Map();
|
||||
const mountedIDs: Set<number> = new Set();
|
||||
const pendingMountIDs: Set<number> = new Set();
|
||||
const pendingUnmountIDs: Set<number> = new Set();
|
||||
const rootIDs: Set<number> = new Set();
|
||||
|
||||
function getID(internalInstance: InternalInstance): number {
|
||||
if (!internalInstanceToIDMap.has(internalInstance)) {
|
||||
const id = getUID();
|
||||
internalInstanceToIDMap.set(internalInstance, id);
|
||||
idToInternalInstanceMap.set(id, internalInstance);
|
||||
}
|
||||
return ((internalInstanceToIDMap.get(internalInstance): any): number);
|
||||
}
|
||||
|
||||
function getChildIDs(internalInstance: InternalInstance): Array<number> {
|
||||
return getChildren(internalInstance).map(getID);
|
||||
}
|
||||
|
||||
//function getParentID(internalInstance: InternalInstance): number {
|
||||
// return getID(internalInstance._hostParent);
|
||||
//}
|
||||
|
||||
function findNearestAncestorInTree(
|
||||
internalInstance: InternalInstance
|
||||
): number | null {
|
||||
let current = internalInstance;
|
||||
while (current != null) {
|
||||
const id = getID(current);
|
||||
if (
|
||||
rootIDs.has(id) ||
|
||||
getElementType(current) !== ElementTypeOtherOrUnknown
|
||||
) {
|
||||
return id;
|
||||
}
|
||||
const parentID = idToParentIDMap.get(id);
|
||||
current = parentID != null ? idToInternalInstanceMap.get(parentID) : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let getInternalIDFromNative: GetInternalIDFromNative = ((null: any): GetInternalIDFromNative);
|
||||
let getNativeFromInternal: (
|
||||
id: number
|
||||
) => ?NativeType = ((null: any): GetNativeFromInternal);
|
||||
|
||||
// React Native
|
||||
if (renderer.Mount.findNodeHandle && renderer.Mount.nativeTagToRootNodeID) {
|
||||
getInternalIDFromNative = (nativeTag, findNearestUnfilteredAncestor) => {
|
||||
const internalInstance = renderer.Mount.nativeTagToRootNodeID(nativeTag);
|
||||
return findNearestAncestorInTree(internalInstance);
|
||||
};
|
||||
getNativeFromInternal = (id: number) => {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
return renderer.Mount.findNodeHandle(internalInstance);
|
||||
};
|
||||
|
||||
// React DOM 15+
|
||||
} else if (renderer.ComponentTree) {
|
||||
getInternalIDFromNative = (node, findNearestUnfilteredAncestor) => {
|
||||
const internalInstance = renderer.ComponentTree.getClosestInstanceFromNode(
|
||||
node
|
||||
);
|
||||
return findNearestAncestorInTree(internalInstance);
|
||||
};
|
||||
getNativeFromInternal = (id: number) => {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
return renderer.ComponentTree.getNodeFromInstance(internalInstance);
|
||||
};
|
||||
|
||||
// React DOM
|
||||
} else if (renderer.Mount.getID && renderer.Mount.getNode) {
|
||||
getInternalIDFromNative = (node, findNearestUnfilteredAncestor) => {
|
||||
let id = renderer.Mount.getID(node);
|
||||
while (node && node.parentNode && !id) {
|
||||
node = node.parentNode;
|
||||
id = renderer.Mount.getID(node);
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
getNativeFromInternal = (id: number) => {
|
||||
try {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
if (internalInstance != null) {
|
||||
return renderer.Mount.getNode(internalInstance._rootNodeID);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return null;
|
||||
};
|
||||
} else {
|
||||
console.warn(
|
||||
'Unknown React version (does not have getID), probably an unshimmed React Native'
|
||||
);
|
||||
}
|
||||
|
||||
let oldReconcilerMethods = null;
|
||||
let oldRenderComponent = null;
|
||||
let oldRenderRoot = null;
|
||||
|
||||
// React DOM
|
||||
if (renderer.Mount._renderNewRootComponent) {
|
||||
oldRenderRoot = decorateResult(
|
||||
renderer.Mount,
|
||||
'_renderNewRootComponent',
|
||||
internalInstance => {
|
||||
const id = getID(internalInstance);
|
||||
|
||||
rootIDs.add(id);
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.log('renderer.Mount._renderNewRootComponent()', id);
|
||||
}
|
||||
|
||||
recordPendingMount(internalInstance);
|
||||
|
||||
// If we're mounting a root, we've just finished a batch of work,
|
||||
// so it's safe to synchronously flush.
|
||||
flushPendingEvents(id);
|
||||
}
|
||||
);
|
||||
|
||||
// React Native
|
||||
} else if (renderer.Mount.renderComponent) {
|
||||
oldRenderComponent = decorateResult(
|
||||
renderer.Mount,
|
||||
'renderComponent',
|
||||
internalInstance => {
|
||||
const id = getID(internalInstance);
|
||||
|
||||
rootIDs.add(id);
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.log('renderer.Mount.renderComponent()', id);
|
||||
}
|
||||
|
||||
recordPendingMount(internalInstance);
|
||||
|
||||
// If we're mounting a root, we've just finished a batch of work,
|
||||
// so it's safe to synchronously flush.
|
||||
flushPendingEvents(id);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (renderer.Reconciler) {
|
||||
oldReconcilerMethods = decorateMany(renderer.Reconciler, {
|
||||
mountComponent(internalInstance, rootID, transaction, context) {
|
||||
recordPendingMount(internalInstance);
|
||||
},
|
||||
performUpdateIfNecessary(
|
||||
internalInstance,
|
||||
nextChild,
|
||||
transaction,
|
||||
context
|
||||
) {
|
||||
// TODO Check for change in order of children
|
||||
},
|
||||
receiveComponent(internalInstance, nextChild, transaction, context) {
|
||||
// TODO Check for change in order of children
|
||||
},
|
||||
unmountComponent(internalInstance) {
|
||||
recordPendingUnmount(internalInstance);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
if (oldReconcilerMethods !== null) {
|
||||
if (renderer.Component) {
|
||||
restoreMany(renderer.Component.Mixin, oldReconcilerMethods);
|
||||
} else {
|
||||
restoreMany(renderer.Reconciler, oldReconcilerMethods);
|
||||
}
|
||||
}
|
||||
if (oldRenderRoot !== null) {
|
||||
renderer.Mount._renderNewRootComponent = oldRenderRoot;
|
||||
}
|
||||
if (oldRenderComponent !== null) {
|
||||
renderer.Mount.renderComponent = oldRenderComponent;
|
||||
}
|
||||
oldReconcilerMethods = null;
|
||||
oldRenderRoot = null;
|
||||
oldRenderComponent = null;
|
||||
}
|
||||
|
||||
let pendingOperations: Uint32Array = new Uint32Array(0);
|
||||
|
||||
function addOperation(
|
||||
newAction: Uint32Array,
|
||||
addToStartOfQueue: boolean = false
|
||||
): void {
|
||||
const oldActions = pendingOperations;
|
||||
pendingOperations = new Uint32Array(oldActions.length + newAction.length);
|
||||
if (addToStartOfQueue) {
|
||||
pendingOperations.set(newAction);
|
||||
pendingOperations.set(oldActions, newAction.length);
|
||||
} else {
|
||||
pendingOperations.set(oldActions);
|
||||
pendingOperations.set(newAction, oldActions.length);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Rethink the below queueing mechanism.
|
||||
// Every mount is some parent's update (except for the root mount which we can explicitly handle)
|
||||
// So maybe we only need to call queueFlushPendingEvents() for updates,
|
||||
// and maybe we can rely on an id-to-root Map for this case, to limit the scope of what we crawl.
|
||||
|
||||
// Older React renderers did not have the concept of a commit.
|
||||
// The data structure was just ad-hoc mutated in place.
|
||||
// So except for the case of the root mounting the first time,
|
||||
// there is no event we can observe to signal that a render is finished.
|
||||
// However since older renderers were always synchronous,
|
||||
// we can use setTimeout to batch operations together.
|
||||
// In the case of a cascading update, we might batch multiple "commits"-
|
||||
// but that should be okay, since the batching is not strictly necessary.
|
||||
let flushPendingEventsTimeoutID: TimeoutID | null = null;
|
||||
function queueFlushPendingEvents() {
|
||||
if (flushPendingEventsTimeoutID === null) {
|
||||
flushPendingEventsTimeoutID = setTimeout(() => {
|
||||
flushPendingEventsTimeoutID = null;
|
||||
|
||||
// If there are pending operations, walk the tree and find them.
|
||||
// Ideally we wouldjust pluck the pending operations out of the sets directly,
|
||||
// but without doing a full traversal, it would be hard for us to determine the filtered parent.
|
||||
// It should be possible to improve this though, by maintaining a map of id-to-parent,
|
||||
// and crawling upward to the first non-filtered node.
|
||||
// TODO Revisit this and think about it more...
|
||||
if (pendingMountIDs.size > 0 || pendingUnmountIDs.size > 0) {
|
||||
rootIDs.forEach(flushPendingEvents);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function flushInitialOperations() {
|
||||
// Older versions of React do not support profiling mode, so there's nothing to flush.
|
||||
// Crawl roots though and register any nodes that mounted before we were injected.
|
||||
|
||||
const roots =
|
||||
renderer.Mount._instancesByReactRootID ||
|
||||
renderer.Mount._instancesByContainerID;
|
||||
|
||||
for (let key in roots) {
|
||||
const internalInstance = roots[key];
|
||||
const id = getID(internalInstance);
|
||||
|
||||
rootIDs.add(id);
|
||||
|
||||
crawlAndRecordMounts(id, 0, true);
|
||||
|
||||
// It's safe to synchronously flush for the root we just crawled.
|
||||
flushPendingEvents(id);
|
||||
}
|
||||
}
|
||||
|
||||
function crawlAndRecordMounts(
|
||||
id: number,
|
||||
parentID: number,
|
||||
isInitialMount: boolean
|
||||
) {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
const shouldIncludeInTree =
|
||||
parentID === 0 ||
|
||||
getElementType(internalInstance) !== ElementTypeOtherOrUnknown;
|
||||
|
||||
// Not all nodes are mounted in the frontend DevTools tree,
|
||||
// but it's important to track parent info even for the unmounted ones.
|
||||
idToParentIDMap.set(id, parentID);
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.group(
|
||||
'crawlAndRecordMounts() id:',
|
||||
id,
|
||||
'shouldIncludeInTree?',
|
||||
shouldIncludeInTree
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldIncludeInTree) {
|
||||
const didMount = isInitialMount || pendingMountIDs.has(id);
|
||||
const didUnmount = pendingUnmountIDs.has(id);
|
||||
|
||||
// If this node was both mounted and unmounted in the same batch,
|
||||
// just skip it and don't send any update.
|
||||
if (didMount && didUnmount) {
|
||||
pendingUnmountIDs.delete(id);
|
||||
return;
|
||||
} else if (didMount) {
|
||||
recordMount(id, parentID);
|
||||
}
|
||||
}
|
||||
|
||||
getChildIDs(internalInstance).forEach(childID =>
|
||||
crawlAndRecordMounts(
|
||||
childID,
|
||||
shouldIncludeInTree ? id : parentID,
|
||||
isInitialMount
|
||||
)
|
||||
);
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
function flushPendingEvents(rootID: number): void {
|
||||
// Crawl tree and queue mounts/updates.
|
||||
crawlAndRecordMounts(rootID, 0, false);
|
||||
|
||||
// Send pending deletions.
|
||||
pendingUnmountIDs.forEach(id => {
|
||||
if (mountedIDs.has(id)) {
|
||||
recordUnmount(id);
|
||||
}
|
||||
});
|
||||
|
||||
// Identify which renderer this update is coming from.
|
||||
// This enables roots to be mapped to renderers,
|
||||
// Which in turn enables fiber props, states, and hooks to be inspected.
|
||||
const idArray = new Uint32Array(2);
|
||||
idArray[0] = rendererID;
|
||||
idArray[1] = rootID;
|
||||
addOperation(idArray, true);
|
||||
|
||||
if (__DEBUG__) {
|
||||
operationsArrayToString(pendingOperations);
|
||||
}
|
||||
|
||||
// If we've already connected to the frontend, just pass the operations through.
|
||||
hook.emit('operations', pendingOperations);
|
||||
|
||||
pendingMountIDs.clear();
|
||||
pendingUnmountIDs.clear();
|
||||
pendingOperations = new Uint32Array(0);
|
||||
}
|
||||
|
||||
function inspectElement(id: number): InspectedElement | null {
|
||||
let result = inspectElementRaw(id);
|
||||
if (result === null) {
|
||||
return null;
|
||||
}
|
||||
// TODO Review sanitization approach for the below inspectable values.
|
||||
result.context = cleanForBridge(result.context);
|
||||
result.props = cleanForBridge(result.props);
|
||||
result.state = cleanForBridge(result.state);
|
||||
return result;
|
||||
}
|
||||
|
||||
function inspectElementRaw(id: number): InspectedElement | null {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
const data = getData(internalInstance);
|
||||
|
||||
let context = null;
|
||||
let owners = null;
|
||||
let props = null;
|
||||
let state = null;
|
||||
let source = null;
|
||||
|
||||
if (internalInstance != null) {
|
||||
const element = internalInstance._currentElement;
|
||||
if (element !== null) {
|
||||
props = element.props;
|
||||
source = element._source != null ? element._source : null;
|
||||
|
||||
let owner = element._owner;
|
||||
if (owner) {
|
||||
owners = [];
|
||||
while (owner != null) {
|
||||
owners.push({
|
||||
displayName: getData(owner).displayName || 'Unknown',
|
||||
id: getID(owner),
|
||||
});
|
||||
owner = owner.owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
context = internalInstance.context || null;
|
||||
state = internalInstance.state || null;
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
|
||||
// Hooks did not exist in legacy versions
|
||||
canEditHooks: false,
|
||||
|
||||
// Does the current renderer support editable function props?
|
||||
canEditFunctionProps: true,
|
||||
|
||||
// Suspense did not exist in legacy versions
|
||||
canToggleSuspense: false,
|
||||
|
||||
// Can view component source location.
|
||||
canViewSource:
|
||||
data.type === ElementTypeClass || data.type === ElementTypeFunction,
|
||||
|
||||
displayName: data.displayName,
|
||||
|
||||
// Inspectable properties.
|
||||
context,
|
||||
hooks: null,
|
||||
props,
|
||||
state,
|
||||
|
||||
// List of owners
|
||||
owners,
|
||||
|
||||
// Location of component in source coude.
|
||||
source,
|
||||
};
|
||||
}
|
||||
|
||||
function logElementToConsole(id: number): void {
|
||||
const result = inspectElementRaw(id);
|
||||
if (result === null) {
|
||||
console.warn(`Could not find element with id "${id}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
const supportsGroup = typeof console.groupCollapsed === 'function';
|
||||
if (supportsGroup) {
|
||||
console.groupCollapsed(
|
||||
`[Click to expand] %c<${result.displayName || 'Component'} />`,
|
||||
// --dom-tag-name-color is the CSS variable Chrome styles HTML elements with in the console.
|
||||
'color: var(--dom-tag-name-color); font-weight: normal;'
|
||||
);
|
||||
}
|
||||
if (result.props !== null) {
|
||||
console.log('Props:', result.props);
|
||||
}
|
||||
if (result.state !== null) {
|
||||
console.log('State:', result.state);
|
||||
}
|
||||
if (result.context !== null) {
|
||||
console.log('State:', result.context);
|
||||
}
|
||||
const nativeNode = getNativeFromInternal(id);
|
||||
if (nativeNode !== null) {
|
||||
console.log('Node:', nativeNode);
|
||||
}
|
||||
if (window.chrome || /firefox/i.test(navigator.userAgent)) {
|
||||
console.log(
|
||||
'Right-click any value to save it as a global variable for further inspection.'
|
||||
);
|
||||
}
|
||||
if (supportsGroup) {
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
function prepareViewElementSource(id: number): void {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
if (internalInstance == null) {
|
||||
console.warn(`Could not find instance with id "${id}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
const element = internalInstance._currentElement;
|
||||
if (element == null) {
|
||||
console.warn(`Could not find element with id "${id}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
global.$type = element.type;
|
||||
}
|
||||
|
||||
function selectElement(id: number): void {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
if (internalInstance == null) {
|
||||
console.warn(`Could not find instance with id "${id}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (getElementType(internalInstance)) {
|
||||
case ElementTypeClass:
|
||||
case ElementTypeFunction:
|
||||
const element = internalInstance._currentElement;
|
||||
if (element == null) {
|
||||
console.warn(`Could not find element with id "${id}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
global.$r = {
|
||||
props: element.props,
|
||||
type: element.type,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function recordPendingMount(internalInstance: InternalInstance) {
|
||||
pendingMountIDs.add(getID(internalInstance));
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.log(
|
||||
'%crecordPendingMount()',
|
||||
'color: green',
|
||||
getID(internalInstance)
|
||||
);
|
||||
}
|
||||
|
||||
queueFlushPendingEvents();
|
||||
}
|
||||
|
||||
function recordPendingUnmount(internalInstance: InternalInstance) {
|
||||
const id = getID(internalInstance);
|
||||
|
||||
pendingUnmountIDs.add(id);
|
||||
|
||||
// Not all nodes are mounted (or unmounted) in the frontend DevTools tree,
|
||||
// so it's important to remove entries from this map on pending unmount.
|
||||
idToParentIDMap.delete(id);
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.log(
|
||||
'%crecordPendingUnmount()',
|
||||
'color: red',
|
||||
getID(internalInstance)
|
||||
);
|
||||
}
|
||||
|
||||
queueFlushPendingEvents();
|
||||
}
|
||||
|
||||
function recordMount(id: number, parentID: number) {
|
||||
const internalInstance = ((idToInternalInstanceMap.get(
|
||||
id
|
||||
): any): InternalInstance);
|
||||
const isRoot = rootIDs.has(id);
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.log(
|
||||
'%crecordMount()',
|
||||
'color: green; font-weight: bold;',
|
||||
id,
|
||||
getData(internalInstance).displayName
|
||||
);
|
||||
}
|
||||
|
||||
mountedIDs.add(id);
|
||||
|
||||
if (isRoot) {
|
||||
// TODO Is this right? For all versions?
|
||||
const hasOwnerMetadata =
|
||||
internalInstance._currentElement != null &&
|
||||
internalInstance._currentElement._owner != null;
|
||||
|
||||
const operation = new Uint32Array(5);
|
||||
operation[0] = TREE_OPERATION_ADD;
|
||||
operation[1] = id;
|
||||
operation[2] = ElementTypeRoot;
|
||||
operation[3] = 0; // isProfilingSupported?
|
||||
operation[4] = hasOwnerMetadata ? 1 : 0;
|
||||
addOperation(operation);
|
||||
} else {
|
||||
const { displayName, key, type } = getData(internalInstance);
|
||||
|
||||
const ownerID =
|
||||
internalInstance._currentElement != null &&
|
||||
internalInstance._currentElement._owner != null
|
||||
? getID(internalInstance._currentElement._owner)
|
||||
: 0;
|
||||
|
||||
let encodedDisplayName = ((null: any): Uint8Array);
|
||||
let encodedKey = ((null: any): Uint8Array);
|
||||
|
||||
if (displayName !== null) {
|
||||
encodedDisplayName = utfEncodeString(displayName);
|
||||
}
|
||||
|
||||
if (key !== null) {
|
||||
// React$Key supports string and number types as inputs,
|
||||
// But React converts numeric keys to strings, so we only have to handle that type here.
|
||||
// https://github.com/facebook/react/blob/0e67969cb1ad8c27a72294662e68fa5d7c2c9783/packages/react/src/ReactElement.js#L187
|
||||
encodedKey = utfEncodeString(((key: any): string));
|
||||
}
|
||||
|
||||
const encodedDisplayNameSize =
|
||||
displayName === null ? 0 : encodedDisplayName.length;
|
||||
const encodedKeySize = key === null ? 0 : encodedKey.length;
|
||||
|
||||
const operation = new Uint32Array(
|
||||
7 + encodedDisplayNameSize + encodedKeySize
|
||||
);
|
||||
operation[0] = TREE_OPERATION_ADD;
|
||||
operation[1] = id;
|
||||
operation[2] = type;
|
||||
operation[3] = parentID;
|
||||
operation[4] = ownerID;
|
||||
operation[5] = encodedDisplayNameSize;
|
||||
if (displayName !== null) {
|
||||
operation.set(encodedDisplayName, 6);
|
||||
}
|
||||
operation[6 + encodedDisplayNameSize] = encodedKeySize;
|
||||
if (key !== null) {
|
||||
operation.set(encodedKey, 6 + encodedDisplayNameSize + 1);
|
||||
}
|
||||
addOperation(operation);
|
||||
}
|
||||
}
|
||||
|
||||
function recordUnmount(id: number) {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
const isRoot = rootIDs.has(id);
|
||||
|
||||
if (__DEBUG__) {
|
||||
console.log(
|
||||
'%crecordUnmount()',
|
||||
'color: red; font-weight: bold;',
|
||||
id,
|
||||
getData(internalInstance).displayName
|
||||
);
|
||||
}
|
||||
|
||||
if (isRoot) {
|
||||
const operation = new Uint32Array(2);
|
||||
operation[0] = TREE_OPERATION_REMOVE;
|
||||
operation[1] = id;
|
||||
addOperation(operation);
|
||||
|
||||
rootIDs.delete(id);
|
||||
} else {
|
||||
const operation = new Uint32Array(2);
|
||||
operation[0] = TREE_OPERATION_REMOVE;
|
||||
operation[1] = id;
|
||||
addOperation(operation);
|
||||
}
|
||||
|
||||
idToInternalInstanceMap.delete(id);
|
||||
internalInstanceToIDMap.delete(internalInstance);
|
||||
|
||||
mountedIDs.delete(id);
|
||||
}
|
||||
|
||||
function setInProps(id: number, path: Array<string | number>, value: any) {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
if (internalInstance != null) {
|
||||
const element = internalInstance._currentElement;
|
||||
internalInstance._currentElement = {
|
||||
...element,
|
||||
props: copyWithSet(element.props, path, value),
|
||||
};
|
||||
forceUpdate(internalInstance._instance);
|
||||
}
|
||||
}
|
||||
|
||||
function setInState(id: number, path: Array<string | number>, value: any) {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
if (internalInstance != null) {
|
||||
setIn(internalInstance.state, path, value);
|
||||
internalInstance.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
function setInContext(id: number, path: Array<string | number>, value: any) {
|
||||
const internalInstance = idToInternalInstanceMap.get(id);
|
||||
if (internalInstance != null) {
|
||||
setIn(internalInstance.context, path, value);
|
||||
forceUpdate(internalInstance);
|
||||
}
|
||||
}
|
||||
|
||||
function setIn(obj: Object, path: Array<string | number>, value: any) {
|
||||
const last = path.pop();
|
||||
const parent = path.reduce(
|
||||
// $FlowFixMe
|
||||
(reduced, attr) => (reduced ? reduced[attr] : null),
|
||||
obj
|
||||
);
|
||||
if (parent) {
|
||||
// $FlowFixMe
|
||||
parent[last] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// v16+ only features
|
||||
const getCommitDetails = () => {
|
||||
throw new Error('getCommitDetails not supported by this renderer');
|
||||
};
|
||||
const getFiberCommits = () => {
|
||||
throw new Error('getFiberCommits not supported by this renderer');
|
||||
};
|
||||
const getInteractions = () => {
|
||||
throw new Error('getInteractions not supported by this renderer');
|
||||
};
|
||||
const getProfilingDataForDownload = () => {
|
||||
throw new Error(
|
||||
'getProfilingDataForDownload not supported by this renderer'
|
||||
);
|
||||
};
|
||||
const getProfilingSummary = () => {
|
||||
throw new Error('getProfilingSummary not supported by this renderer');
|
||||
};
|
||||
const handleCommitFiberRoot = () => {
|
||||
throw new Error('handleCommitFiberRoot not supported by this renderer');
|
||||
};
|
||||
const handleCommitFiberUnmount = () => {
|
||||
throw new Error('handleCommitFiberUnmount not supported by this renderer');
|
||||
};
|
||||
const overrideSuspense = () => {
|
||||
throw new Error('overrideSuspense not supported by this renderer');
|
||||
};
|
||||
const setInHook = () => {
|
||||
throw new Error('setInHook not supported by this renderer');
|
||||
};
|
||||
const startProfiling = () => {
|
||||
throw new Error('startProfiling not supported by this renderer');
|
||||
};
|
||||
const stopProfiling = () => {
|
||||
throw new Error('stopProfiling not supported by this renderer');
|
||||
};
|
||||
|
||||
return {
|
||||
cleanup,
|
||||
flushInitialOperations,
|
||||
getCommitDetails,
|
||||
getFiberCommits,
|
||||
getInteractions,
|
||||
getInternalIDFromNative,
|
||||
getNativeFromInternal,
|
||||
getProfilingDataForDownload,
|
||||
getProfilingSummary,
|
||||
handleCommitFiberRoot,
|
||||
handleCommitFiberUnmount,
|
||||
inspectElement,
|
||||
logElementToConsole,
|
||||
overrideSuspense,
|
||||
prepareViewElementSource,
|
||||
renderer,
|
||||
selectElement,
|
||||
setInContext,
|
||||
setInHook,
|
||||
setInProps,
|
||||
setInState,
|
||||
startProfiling,
|
||||
stopProfiling,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// @flow
|
||||
|
||||
const SEPARATOR = '.';
|
||||
const SUBSEPARATOR = ':';
|
||||
|
||||
const FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
|
||||
// The Symbol used to tag the ReactElement type. If there is no native Symbol
|
||||
// nor polyfill, then a plain number is used for performance.
|
||||
const ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
|
||||
const REACT_ELEMENT_TYPE =
|
||||
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
|
||||
0xeac7;
|
||||
|
||||
/**
|
||||
* Escape and wrap key so it is safe to use as a reactid
|
||||
*
|
||||
* @param {string} key to be escaped.
|
||||
* @return {string} the escaped key.
|
||||
*/
|
||||
function escape(key: string): string {
|
||||
const escapeRegex = /[=:]/g;
|
||||
const escaperLookup = {
|
||||
'=': '=0',
|
||||
':': '=2',
|
||||
};
|
||||
const escapedString = ('' + key).replace(escapeRegex, function(match) {
|
||||
return escaperLookup[match];
|
||||
});
|
||||
|
||||
return '$' + escapedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key string that identifies a component within a set.
|
||||
*
|
||||
* @param {*} component A component that could contain a manual key.
|
||||
* @param {number} index Index that is used if a manual key is not provided.
|
||||
* @return {string}
|
||||
*/
|
||||
function getComponentKey(component, index) {
|
||||
// Do some typechecking here since we call this blindly. We want to ensure
|
||||
// that we don't block potential future ES APIs.
|
||||
if (
|
||||
typeof component === 'object' &&
|
||||
component !== null &&
|
||||
component.key != null
|
||||
) {
|
||||
// Explicit key
|
||||
return escape(component.key);
|
||||
}
|
||||
// Implicit key determined by the index in the set
|
||||
return index.toString(36);
|
||||
}
|
||||
|
||||
/**
|
||||
* We do a copied the 'traverseAllChildrenImpl' method from
|
||||
* `React.Children` so that we don't pull in the whole React library.
|
||||
* @param {?*} children Children tree container.
|
||||
* @param {!string} nameSoFar Name of the key path so far.
|
||||
* @param {!function} callback Callback to invoke with each child found.
|
||||
* @param {?*} traverseContext Used to pass information throughout the traversal
|
||||
* process.
|
||||
* @return {!number} The number of children in this subtree.
|
||||
*/
|
||||
export default function traverseAllChildrenImpl(
|
||||
children: any,
|
||||
nameSoFar: string,
|
||||
callback: Function,
|
||||
traverseContext: any
|
||||
): number {
|
||||
const type = typeof children;
|
||||
|
||||
if (type === 'undefined' || type === 'boolean') {
|
||||
// All of the above are perceived as null.
|
||||
children = null;
|
||||
}
|
||||
|
||||
if (
|
||||
children === null ||
|
||||
type === 'string' ||
|
||||
type === 'number' ||
|
||||
// The following is inlined from ReactElement. This means we can optimize
|
||||
// some checks. React Fiber also inlines this logic for similar purposes.
|
||||
(type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
|
||||
) {
|
||||
callback(
|
||||
traverseContext,
|
||||
children,
|
||||
// If it's the only child, treat the name as if it was wrapped in an array
|
||||
// so that it's consistent if the number of children grows.
|
||||
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let child;
|
||||
let nextName;
|
||||
let subtreeCount = 0; // Count of children found in the current subtree.
|
||||
let nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
|
||||
|
||||
if (Array.isArray(children)) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
child = children[i];
|
||||
nextName = nextNamePrefix + getComponentKey(child, i);
|
||||
subtreeCount += traverseAllChildrenImpl(
|
||||
child,
|
||||
nextName,
|
||||
callback,
|
||||
traverseContext
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let iteratorFn =
|
||||
(ITERATOR_SYMBOL && children[ITERATOR_SYMBOL]) ||
|
||||
children[FAUX_ITERATOR_SYMBOL];
|
||||
if (typeof iteratorFn === 'function') {
|
||||
let iterator = iteratorFn.call(children);
|
||||
let step;
|
||||
let ii = 0;
|
||||
while (!(step = iterator.next()).done) {
|
||||
child = step.value;
|
||||
nextName = nextNamePrefix + getComponentKey(child, ii++);
|
||||
subtreeCount += traverseAllChildrenImpl(
|
||||
child,
|
||||
nextName,
|
||||
callback,
|
||||
traverseContext
|
||||
);
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
let addendum =
|
||||
' If you meant to render a collection of children, use an array ' +
|
||||
'instead.';
|
||||
let childrenString = '' + children;
|
||||
throw Error(
|
||||
`The React Devtools cannot render an object as a child. (found: ${
|
||||
childrenString === '[object Object]'
|
||||
? 'object with keys {' + Object.keys(children).join(', ') + '}'
|
||||
: childrenString
|
||||
}). ${addendum}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return subtreeCount;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// @flow
|
||||
|
||||
import type { InternalInstance } from './renderer';
|
||||
|
||||
export function decorateResult(
|
||||
object: Object,
|
||||
attr: string,
|
||||
fn: Function
|
||||
): Function {
|
||||
const old = object[attr];
|
||||
object[attr] = function(instance: InternalInstance) {
|
||||
const res = old.apply(this, arguments);
|
||||
fn(res);
|
||||
return res;
|
||||
};
|
||||
return old;
|
||||
}
|
||||
|
||||
export function decorate(object: Object, attr: string, fn: Function): Function {
|
||||
const old = object[attr];
|
||||
object[attr] = function(instance: InternalInstance) {
|
||||
const res = old.apply(this, arguments);
|
||||
fn.apply(this, arguments);
|
||||
return res;
|
||||
};
|
||||
return old;
|
||||
}
|
||||
|
||||
export function decorateMany(
|
||||
source: Object,
|
||||
fns: { [attr: string]: Function }
|
||||
): Object {
|
||||
const olds = {};
|
||||
for (const name in fns) {
|
||||
olds[name] = decorate(source, name, fns[name]);
|
||||
}
|
||||
return olds;
|
||||
}
|
||||
|
||||
export function restoreMany(source: Object, olds: Object): void {
|
||||
for (let name in olds) {
|
||||
source[name] = olds[name];
|
||||
}
|
||||
}
|
||||
|
||||
export function forceUpdate(instance: InternalInstance): void {
|
||||
if (typeof instance.forceUpdate === 'function') {
|
||||
instance.forceUpdate();
|
||||
} else if (
|
||||
instance.updater != null &&
|
||||
typeof instance.updater.enqueueForceUpdate === 'function'
|
||||
) {
|
||||
instance.updater.enqueueForceUpdate(this, () => {}, 'forceUpdate');
|
||||
}
|
||||
}
|
||||
+17
-10
@@ -14,7 +14,7 @@ import {
|
||||
ElementTypeRoot,
|
||||
ElementTypeSuspense,
|
||||
} from 'src/devtools/types';
|
||||
import { getDisplayName, utfEncodeString } from '../utils';
|
||||
import { getDisplayName, getUID, utfEncodeString } from '../utils';
|
||||
import { cleanForBridge, copyWithSet, setInObject } from './utils';
|
||||
import {
|
||||
__DEBUG__,
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN,
|
||||
TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
|
||||
} from '../constants';
|
||||
import { getUID } from '../utils';
|
||||
import { inspectHooksOfFiber } from './ReactDebugHooks';
|
||||
|
||||
import type {
|
||||
@@ -312,8 +311,6 @@ export function attach(
|
||||
: symbolOrNumber;
|
||||
}
|
||||
|
||||
// TODO: we might want to change the data structure once we no longer suppport Stack versions of `getData`.
|
||||
// TODO: Keep in sync with getElementType()
|
||||
function getDataForFiber(fiber: Fiber): FiberData {
|
||||
const { elementType, type, key, tag } = fiber;
|
||||
|
||||
@@ -1166,7 +1163,7 @@ export function attach(
|
||||
currentRootID = -1;
|
||||
}
|
||||
|
||||
function findNativeByFiberID(id: number) {
|
||||
function getNativeFromInternal(id: number) {
|
||||
try {
|
||||
const fiber = findCurrentFiberUsingSlowPath(idToFiberMap.get(id));
|
||||
if (fiber === null) {
|
||||
@@ -1190,7 +1187,7 @@ export function attach(
|
||||
}
|
||||
}
|
||||
|
||||
function getFiberIDFromNative(
|
||||
function getInternalIDFromNative(
|
||||
hostInstance,
|
||||
findNearestUnfilteredAncestor = false
|
||||
) {
|
||||
@@ -1441,6 +1438,14 @@ export function attach(
|
||||
case ForwardRef:
|
||||
global.$type = fiber.type.render;
|
||||
break;
|
||||
case MemoComponent:
|
||||
case SimpleMemoComponent:
|
||||
const { elementType, type } = fiber;
|
||||
global.$type =
|
||||
elementType != null && elementType.type != null
|
||||
? elementType.type
|
||||
: type;
|
||||
break;
|
||||
default:
|
||||
global.$type = null;
|
||||
break;
|
||||
@@ -1483,7 +1488,9 @@ export function attach(
|
||||
tag === FunctionComponent ||
|
||||
tag === IncompleteClassComponent ||
|
||||
tag === IndeterminateComponent ||
|
||||
tag === ForwardRef
|
||||
tag === MemoComponent ||
|
||||
tag === ForwardRef ||
|
||||
tag === SimpleMemoComponent
|
||||
) {
|
||||
canViewSource = true;
|
||||
if (stateNode && stateNode.context != null) {
|
||||
@@ -1623,7 +1630,7 @@ export function attach(
|
||||
if (result.hooks !== null) {
|
||||
console.log('Hooks:', result.hooks);
|
||||
}
|
||||
const nativeNode = findNativeByFiberID(id);
|
||||
const nativeNode = getNativeFromInternal(id);
|
||||
if (nativeNode !== null) {
|
||||
console.log('Node:', nativeNode);
|
||||
}
|
||||
@@ -1938,10 +1945,10 @@ export function attach(
|
||||
cleanup,
|
||||
flushInitialOperations,
|
||||
getCommitDetails,
|
||||
getFiberIDFromNative,
|
||||
getInternalIDFromNative,
|
||||
getFiberCommits,
|
||||
getInteractions,
|
||||
findNativeByFiberID,
|
||||
getNativeFromInternal,
|
||||
getProfilingDataForDownload,
|
||||
getProfilingSummary,
|
||||
handleCommitFiberRoot,
|
||||
|
||||
+11
-8
@@ -19,7 +19,7 @@ export type FiberData = {|
|
||||
type: ElementType,
|
||||
|};
|
||||
|
||||
export type NativeType = {};
|
||||
export type NativeType = Object;
|
||||
export type RendererID = number;
|
||||
|
||||
type Dispatcher = any;
|
||||
@@ -90,17 +90,20 @@ export type ProfilingSummary = {|
|
||||
rootID: number,
|
||||
|};
|
||||
|
||||
export type GetInternalIDFromNative = (
|
||||
component: NativeType,
|
||||
findNearestUnfilteredAncestor?: boolean
|
||||
) => number | null;
|
||||
export type GetNativeFromInternal = (id: number) => ?NativeType;
|
||||
|
||||
export type RendererInterface = {
|
||||
cleanup: () => void,
|
||||
findNativeByFiberID: (id: number) => ?NativeType,
|
||||
flushInitialOperations: () => void,
|
||||
getCommitDetails: (rootID: number, commitIndex: number) => CommitDetails,
|
||||
getFiberIDFromNative: (
|
||||
component: NativeType,
|
||||
findNearestUnfilteredAncestor?: boolean
|
||||
) => number | null,
|
||||
getFiberCommits: (rootID: number, fiberID: number) => FiberCommits,
|
||||
getInteractions: (rootID: number) => Interactions,
|
||||
getInternalIDFromNative: GetInternalIDFromNative,
|
||||
getNativeFromInternal: GetNativeFromInternal,
|
||||
getProfilingDataForDownload: (rootID: number) => Object,
|
||||
getProfilingSummary: (rootID: number) => ProfilingSummary,
|
||||
handleCommitFiberRoot: (fiber: Object) => void,
|
||||
@@ -112,14 +115,14 @@ export type RendererInterface = {
|
||||
renderer: ReactRenderer | null,
|
||||
selectElement: (id: number) => void,
|
||||
setInContext: (id: number, path: Array<string | number>, value: any) => void,
|
||||
setInProps: (id: number, path: Array<string | number>, value: any) => void,
|
||||
setInState: (id: number, path: Array<string | number>, value: any) => void,
|
||||
setInHook: (
|
||||
id: number,
|
||||
index: number,
|
||||
path: Array<string | number>,
|
||||
value: any
|
||||
) => void,
|
||||
setInProps: (id: number, path: Array<string | number>, value: any) => void,
|
||||
setInState: (id: number, path: Array<string | number>, value: any) => void,
|
||||
startProfiling: () => void,
|
||||
stopProfiling: () => void,
|
||||
};
|
||||
|
||||
+104
-1
@@ -1,6 +1,16 @@
|
||||
// @flow
|
||||
|
||||
const LRU = require('lru-cache');
|
||||
import LRU from 'lru-cache';
|
||||
import {
|
||||
TREE_OPERATION_ADD,
|
||||
TREE_OPERATION_REMOVE,
|
||||
TREE_OPERATION_RESET_CHILDREN,
|
||||
TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN,
|
||||
TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
|
||||
} from './constants';
|
||||
import { ElementTypeRoot } from 'src/devtools/types';
|
||||
|
||||
import type { ElementType } from 'src/devtools/types';
|
||||
|
||||
const FB_MODULE_RE = /^(.*) \[from (.*)\]$/;
|
||||
const cachedDisplayNames: WeakMap<Function, string> = new WeakMap();
|
||||
@@ -76,3 +86,96 @@ export function utfEncodeString(string: string): Uint32Array {
|
||||
function toCodePoint(string: string) {
|
||||
return string.codePointAt(0);
|
||||
}
|
||||
|
||||
export function operationsArrayToString(operations: Uint32Array) {
|
||||
const rendererID = operations[0];
|
||||
const rootID = operations[1];
|
||||
|
||||
console.group('rendererID:', rendererID, 'rootID:', rootID);
|
||||
|
||||
let i = 2;
|
||||
while (i < operations.length) {
|
||||
let id: number = ((null: any): number);
|
||||
let parentID: number = ((null: any): number);
|
||||
let type: ElementType = ((null: any): ElementType);
|
||||
|
||||
const operation = operations[i];
|
||||
|
||||
switch (operation) {
|
||||
case TREE_OPERATION_ADD:
|
||||
id = ((operations[i + 1]: any): number);
|
||||
type = ((operations[i + 2]: any): ElementType);
|
||||
|
||||
i = i + 3;
|
||||
|
||||
if (type === ElementTypeRoot) {
|
||||
console.log(`Add root fiber ${id}`);
|
||||
|
||||
i++; // supportsProfiling
|
||||
i++; // hasOwnerMetadata
|
||||
} else {
|
||||
parentID = ((operations[i]: any): number);
|
||||
i++;
|
||||
|
||||
i++; // ownerID
|
||||
|
||||
const displayNameLength = operations[i];
|
||||
i++;
|
||||
const displayName =
|
||||
displayNameLength === 0
|
||||
? null
|
||||
: utfDecodeString(
|
||||
(operations.slice(i, i + displayNameLength): any)
|
||||
);
|
||||
i += displayNameLength;
|
||||
|
||||
const keyLength = operations[i];
|
||||
i++;
|
||||
i += +keyLength;
|
||||
|
||||
console.log(
|
||||
`Add fiber ${id} (${displayName || 'null'}) as child of ${parentID}`
|
||||
);
|
||||
}
|
||||
break;
|
||||
case TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN: {
|
||||
id = ((operations[i + 1]: any): number);
|
||||
|
||||
i = i + 2;
|
||||
|
||||
console.log(`Recursively remove children from fiber ${id}`);
|
||||
break;
|
||||
}
|
||||
case TREE_OPERATION_REMOVE: {
|
||||
id = ((operations[i + 1]: any): number);
|
||||
|
||||
i = i + 2;
|
||||
|
||||
console.log(`Remove fiber ${id}`);
|
||||
break;
|
||||
}
|
||||
case TREE_OPERATION_RESET_CHILDREN:
|
||||
id = ((operations[i + 1]: any): number);
|
||||
const numChildren = ((operations[i + 2]: any): number);
|
||||
const children = ((operations.slice(
|
||||
i + 3,
|
||||
i + 3 + numChildren
|
||||
): any): Array<number>);
|
||||
|
||||
i = i + 3 + numChildren;
|
||||
|
||||
console.log(`Re-order fiber ${id} children ${children.join(',')}`);
|
||||
break;
|
||||
case TREE_OPERATION_UPDATE_TREE_BASE_DURATION:
|
||||
// Base duration updates are only sent while profiling is in progress.
|
||||
// We can ignore them at this point.
|
||||
// The profiler UI uses them lazily in order to generate the tree.
|
||||
i = i + 3;
|
||||
break;
|
||||
default:
|
||||
throw Error(`Unsupported Bridge operation ${operation}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user