@samitouri / QOS-React-2 / commits / 7ab93402a8

Initial support for v15+ via new legaacy renderer

Brian Vaughn committed Apr 16, 2019 at 10:22 UTC 7ab93402a8de30fa7e0e885ff40aae2890b901c7
33 files changed +2371 -24
.gitignore
+1 -2
@@ -12,5 +12,4 @@ yarn-error.log
12 .vscode
13 .idea
14 .watchmanconfig
15 -key.pem
16 -
15 +key.pem
\ No newline at end of file
fixtures/regression/14.9.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 14.9</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@0.14.9/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@0.14.9/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/14.9.html">http://localhost:9000/fixtures/regression/14.9.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/15.0.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 15.0</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@15.0/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@15.0/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/15.0.html">http://localhost:9000/fixtures/regression/15.0.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/15.1.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 15.1</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@15.1/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@15.1/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/15.1.html">http://localhost:9000/fixtures/regression/15.1.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/15.2.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 15.2</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@15.2/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@15.2/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/15.2.html">http://localhost:9000/fixtures/regression/15.2.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/15.3.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 15.3</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@15.3/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@15.3/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/15.3.html">http://localhost:9000/fixtures/regression/15.3.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/15.4.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 15.4</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@15.4/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@15.4/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/15.4.html">http://localhost:9000/fixtures/regression/15.4.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/15.5.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 15.5</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@15.5/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@15.5/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/15.5.html">http://localhost:9000/fixtures/regression/15.5.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/15.6.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 15.6</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@15.6/dist/react.js"></script>
15 + <script src="https://unpkg.com/react-dom@15.6/dist/react-dom.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/15.6.html">http://localhost:9000/fixtures/regression/15.6.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/16.0.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.0</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@16.0/umd/react.development.js"></script>
15 + <script src="https://unpkg.com/react-dom@16.0/umd/react-dom.development.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/16.0.html">http://localhost:9000/fixtures/regression/16.0.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/16.1.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.1</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@16.1/umd/react.development.js"></script>
15 + <script src="https://unpkg.com/react-dom@16.1/umd/react-dom.development.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/16.1.html">http://localhost:9000/fixtures/regression/16.1.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/16.2.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.2</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@16.2/umd/react.development.js"></script>
15 + <script src="https://unpkg.com/react-dom@16.2/umd/react-dom.development.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/16.2.html">http://localhost:9000/fixtures/regression/16.2.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/16.3.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.3</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@16.3/umd/react.development.js"></script>
15 + <script src="https://unpkg.com/react-dom@16.3/umd/react-dom.development.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/16.3.html">http://localhost:9000/fixtures/regression/16.3.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/16.4.html new
+39
@@ -0,0 +1,39 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.4</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/react@16.4/umd/react.development.js"></script>
15 + <script src="https://unpkg.com/react-dom@16.4/umd/react-dom.development.js"></script>
16 +
17 + <!-- Don't use this in production: -->
18 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
19 + </head>
20 + <body>
21 + <div id="root">
22 + 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.
23 + <br/><br/>
24 + Use a web server like <code>serve</code> instead:
25 + <br/><br/>
26 + <code>npm install -g pushstate-server</code><br/>
27 + <code>pushstate-server .</code><br/>
28 + <code>open <a href="http://localhost:9000/fixtures/regression/16.4.html">http://localhost:9000/fixtures/regression/16.4.html</a></code>
29 + </div>
30 +
31 + <script src="shared.js" type="text/babel"></script>
32 +
33 + <!--
34 + This is a great way to try React but it's not suitable for production.
35 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
36 + Learn more at https://reactjs.org/docs/getting-started.html
37 + -->
38 + </body>
39 +</html>
\ No newline at end of file
fixtures/regression/16.5.html new
+41
@@ -0,0 +1,41 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.5</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/schedule@0.5.0/umd/schedule.development.js"></script>
15 + <script src="https://unpkg.com/schedule@0.5.0/umd/schedule-tracing.development.js"></script>
16 + <script src="https://unpkg.com/react@16.5/umd/react.development.js"></script>
17 + <script src="https://unpkg.com/react-dom@16.5/umd/react-dom.development.js"></script>
18 +
19 + <!-- Don't use this in production: -->
20 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
21 + </head>
22 + <body>
23 + <div id="root">
24 + 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.
25 + <br/><br/>
26 + Use a web server like <code>serve</code> instead:
27 + <br/><br/>
28 + <code>npm install -g pushstate-server</code><br/>
29 + <code>pushstate-server .</code><br/>
30 + <code>open <a href="http://localhost:9000/fixtures/regression/16.5.html">http://localhost:9000/fixtures/regression/16.5.html</a></code>
31 + </div>
32 +
33 + <script src="shared.js" type="text/babel"></script>
34 +
35 + <!--
36 + This is a great way to try React but it's not suitable for production.
37 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
38 + Learn more at https://reactjs.org/docs/getting-started.html
39 + -->
40 + </body>
41 +</html>
\ No newline at end of file
fixtures/regression/16.6.html new
+42
@@ -0,0 +1,42 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.6</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/scheduler@0.10.0/umd/scheduler.development.js"></script>
15 + <script src="https://unpkg.com/scheduler@0.10.0/umd/scheduler-tracing.development.js"></script>
16 + <script src="https://unpkg.com/react@16.6/umd/react.development.js"></script>
17 + <script src="https://unpkg.com/react-dom@16.6/umd/react-dom.development.js"></script>
18 + <script src="https://unpkg.com/react-cache@2.0.0-alpha.1/umd/react-cache.development.js"></script>
19 +
20 + <!-- Don't use this in production: -->
21 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
22 + </head>
23 + <body>
24 + <div id="root">
25 + 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.
26 + <br/><br/>
27 + Use a web server like <code>serve</code> instead:
28 + <br/><br/>
29 + <code>npm install -g pushstate-server</code><br/>
30 + <code>pushstate-server .</code><br/>
31 + <code>open <a href="http://localhost:9000/fixtures/regression/16.6.html">http://localhost:9000/fixtures/regression/16.6.html</a></code>
32 + </div>
33 +
34 + <script src="shared.js" type="text/babel"></script>
35 +
36 + <!--
37 + This is a great way to try React but it's not suitable for production.
38 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
39 + Learn more at https://reactjs.org/docs/getting-started.html
40 + -->
41 + </body>
42 +</html>
\ No newline at end of file
fixtures/regression/16.7.html new
+42
@@ -0,0 +1,42 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React 16.7</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/scheduler@0.12.0/umd/scheduler.development.js"></script>
15 + <script src="https://unpkg.com/scheduler@0.12.0/umd/scheduler-tracing.development.js"></script>
16 + <script src="https://unpkg.com/react@16.7/umd/react.development.js"></script>
17 + <script src="https://unpkg.com/react-dom@16.7/umd/react-dom.development.js"></script>
18 + <script src="https://unpkg.com/react-cache@2.0.0-alpha.1/umd/react-cache.development.js"></script>
19 +
20 + <!-- Don't use this in production: -->
21 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
22 + </head>
23 + <body>
24 + <div id="root">
25 + 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.
26 + <br/><br/>
27 + Use a web server like <code>serve</code> instead:
28 + <br/><br/>
29 + <code>npm install -g pushstate-server</code><br/>
30 + <code>pushstate-server .</code><br/>
31 + <code>open <a href="http://localhost:9000/fixtures/regression/16.7.html">http://localhost:9000/fixtures/regression/16.7.html</a></code>
32 + </div>
33 +
34 + <script src="shared.js" type="text/babel"></script>
35 +
36 + <!--
37 + This is a great way to try React but it's not suitable for production.
38 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
39 + Learn more at https://reactjs.org/docs/getting-started.html
40 + -->
41 + </body>
42 +</html>
\ No newline at end of file
fixtures/regression/canary.html new
+42
@@ -0,0 +1,42 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React canary</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/scheduler@canary/umd/scheduler.development.js"></script>
15 + <script src="https://unpkg.com/scheduler@canary/umd/scheduler-tracing.development.js"></script>
16 + <script src="https://unpkg.com/react@canary/umd/react.development.js"></script>
17 + <script src="https://unpkg.com/react-dom@canary/umd/react-dom.development.js"></script>
18 + <script src="https://unpkg.com/react-cache@next/umd/react-cache.development.js"></script>
19 +
20 + <!-- Don't use this in production: -->
21 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
22 + </head>
23 + <body>
24 + <div id="root">
25 + 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.
26 + <br/><br/>
27 + Use a web server like <code>serve</code> instead:
28 + <br/><br/>
29 + <code>npm install -g pushstate-server</code><br/>
30 + <code>pushstate-server .</code><br/>
31 + <code>open <a href="http://localhost:9000/fixtures/regression/canary.html">http://localhost:9000/fixtures/regression/canary.html</a></code>
32 + </div>
33 +
34 + <script src="shared.js" type="text/babel"></script>
35 +
36 + <!--
37 + This is a great way to try React but it's not suitable for production.
38 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
39 + Learn more at https://reactjs.org/docs/getting-started.html
40 + -->
41 + </body>
42 +</html>
\ No newline at end of file
fixtures/regression/index.html new
+28
@@ -0,0 +1,28 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React DevTools regression test</title>
6 + <link rel="stylesheet" href="styles.css" />
7 + </head>
8 + <body>
9 + <iframe src="canary.html"></iframe>
10 + <iframe src="next.html"></iframe>
11 + <iframe src="16.7.html"></iframe>
12 + <iframe src="16.6.html"></iframe>
13 + <iframe src="16.5.html"></iframe>
14 + <iframe src="16.4.html"></iframe>
15 + <iframe src="16.3.html"></iframe>
16 + <iframe src="16.2.html"></iframe>
17 + <iframe src="16.1.html"></iframe>
18 + <iframe src="16.0.html"></iframe>
19 + <iframe src="15.6.html"></iframe>
20 + <iframe src="15.5.html"></iframe>
21 + <iframe src="15.4.html"></iframe>
22 + <iframe src="15.3.html"></iframe>
23 + <iframe src="15.2.html"></iframe>
24 + <iframe src="15.1.html"></iframe>
25 + <iframe src="15.0.html"></iframe>
26 + <iframe src="14.9.html"></iframe>
27 + </body>
28 +</html>
\ No newline at end of file
fixtures/regression/next.html new
+42
@@ -0,0 +1,42 @@
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>React next</title>
6 + <link rel="stylesheet" href="styles.css" />
7 +
8 + <script type="text/javascript">
9 + // Enable DevTools to inspect React inside of an <iframe>
10 + // This must run before React is loaded
11 + __REACT_DEVTOOLS_GLOBAL_HOOK__ = parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
12 + </script>
13 +
14 + <script src="https://unpkg.com/scheduler@next/umd/scheduler.development.js"></script>
15 + <script src="https://unpkg.com/scheduler@next/umd/scheduler-tracing.development.js"></script>
16 + <script src="https://unpkg.com/react@next/umd/react.development.js"></script>
17 + <script src="https://unpkg.com/react-dom@next/umd/react-dom.development.js"></script>
18 + <script src="https://unpkg.com/react-cache@next/umd/react-cache.development.js"></script>
19 +
20 + <!-- Don't use this in production: -->
21 + <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
22 + </head>
23 + <body>
24 + <div id="root">
25 + 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.
26 + <br/><br/>
27 + Use a web server like <code>serve</code> instead:
28 + <br/><br/>
29 + <code>npm install -g pushstate-server</code><br/>
30 + <code>pushstate-server .</code><br/>
31 + <code>open <a href="http://localhost:9000/fixtures/regression/next.html">http://localhost:9000/fixtures/regression/next.html</a></code>
32 + </div>
33 +
34 + <script src="shared.js" type="text/babel"></script>
35 +
36 + <!--
37 + This is a great way to try React but it's not suitable for production.
38 + It slowly compiles JSX with Babel in the browser and uses a large development build of React.
39 + Learn more at https://reactjs.org/docs/getting-started.html
40 + -->
41 + </body>
42 +</html>
\ No newline at end of file
fixtures/regression/shared.js new
+330
@@ -0,0 +1,330 @@
1 +/* eslint-disable no-fallthrough, react/react-in-jsx-scope, react/jsx-no-undef */
2 +/* global React ReactCache ReactDOM SchedulerTracing ScheduleTracing */
3 +
4 +const apps = [];
5 +
6 +const pieces = React.version.split('.');
7 +const major =
8 + pieces[0] === '0' ? parseInt(pieces[1], 10) : parseInt(pieces[0], 10);
9 +const minor =
10 + pieces[0] === '0' ? parseInt(pieces[2], 10) : parseInt(pieces[1], 10);
11 +
12 +// Convenience wrapper to organize API features in DevTools.
13 +function Feature({ children, label, version }) {
14 + return (
15 + <div className="Feature">
16 + <div className="FeatureHeader">
17 + <code className="FeatureCode">{label}</code>
18 + <small>{version}</small>
19 + </div>
20 + {children}
21 + </div>
22 + );
23 +}
24 +
25 +// Simplify interaction tracing for tests below.
26 +let trace = null;
27 +if (typeof SchedulerTracing !== 'undefined') {
28 + trace = SchedulerTracing.unstable_trace;
29 +} else if (typeof ScheduleTracing !== 'undefined') {
30 + trace = ScheduleTracing.unstable_trace;
31 +} else {
32 + trace = (_, __, callback) => callback();
33 +}
34 +
35 +// https://github.com/facebook/react/blob/master/CHANGELOG.md
36 +switch (major) {
37 + case 16:
38 + switch (minor) {
39 + case 7:
40 + if (typeof React.useState === 'function') {
41 + // Hooks
42 + function Hooks() {
43 + const [count, setCount] = React.useState(0);
44 + const incrementCount = React.useCallback(
45 + () => setCount(count + 1),
46 + [count]
47 + );
48 + return (
49 + <div>
50 + count: {count}{' '}
51 + <button onClick={incrementCount}>increment</button>
52 + </div>
53 + );
54 + }
55 + apps.push(
56 + <Feature key="Hooks" label="Hooks" version="16.7+">
57 + <Hooks />
58 + </Feature>
59 + );
60 + }
61 + case 6:
62 + // memo
63 + function LabelComponent({ label }) {
64 + return <label>{label}</label>;
65 + }
66 + const AnonymousMemoized = React.memo(({ label }) => (
67 + <label>{label}</label>
68 + ));
69 + const Memoized = React.memo(LabelComponent);
70 + const CustomMemoized = React.memo(LabelComponent);
71 + CustomMemoized.displayName = 'MemoizedLabelFunction';
72 + apps.push(
73 + <Feature key="memo" label="memo" version="16.6+">
74 + <AnonymousMemoized label="AnonymousMemoized" />
75 + <Memoized label="Memoized" />
76 + <CustomMemoized label="CustomMemoized" />
77 + </Feature>
78 + );
79 +
80 + // Suspense
81 + const loadResource = ([text, ms]) => {
82 + return new Promise((resolve, reject) => {
83 + setTimeout(() => {
84 + resolve(text);
85 + }, ms);
86 + });
87 + };
88 + const getResourceKey = ([text, ms]) => text;
89 + const Resource = ReactCache.unstable_createResource(
90 + loadResource,
91 + getResourceKey
92 + );
93 + class Suspending extends React.Component {
94 + state = { useSuspense: false };
95 + useSuspense = () => this.setState({ useSuspense: true });
96 + render() {
97 + if (this.state.useSuspense) {
98 + const text = Resource.read(['loaded', 2000]);
99 + return text;
100 + } else {
101 + return <button onClick={this.useSuspense}>load data</button>;
102 + }
103 + }
104 + }
105 + apps.push(
106 + <Feature key="Suspense" label="Suspense" version="16.6+">
107 + <React.Suspense fallback={<div>loading...</div>}>
108 + <Suspending />
109 + </React.Suspense>
110 + </Feature>
111 + );
112 +
113 + // lazy
114 + const LazyWithDefaultProps = React.lazy(
115 + () =>
116 + new Promise(resolve => {
117 + function FooWithDefaultProps(props) {
118 + return (
119 + <h1>
120 + {props.greeting}, {props.name}
121 + </h1>
122 + );
123 + }
124 + FooWithDefaultProps.defaultProps = {
125 + name: 'World',
126 + greeting: 'Bonjour',
127 + };
128 + resolve({
129 + default: FooWithDefaultProps,
130 + });
131 + })
132 + );
133 + apps.push(
134 + <Feature key="lazy" label="lazy" version="16.6+">
135 + <React.Suspense fallback={<div>loading...</div>}>
136 + <LazyWithDefaultProps greeting="Hello" />
137 + </React.Suspense>
138 + </Feature>
139 + );
140 + case 5:
141 + case 4:
142 + // unstable_Profiler
143 + class ProfilerChild extends React.Component {
144 + state = { count: 0 };
145 + incrementCount = () =>
146 + this.setState(prevState => ({ count: prevState.count + 1 }));
147 + render() {
148 + return (
149 + <div>
150 + count: {this.state.count}{' '}
151 + <button onClick={this.incrementCount}>increment</button>
152 + </div>
153 + );
154 + }
155 + }
156 + const onRender = (...args) => console.log('onRender()', ...args);
157 + const Profiler = React.unstable_Profiler || React.Profiler;
158 + apps.push(
159 + <Feature
160 + key="unstable_Profiler"
161 + label="unstable_Profiler"
162 + version="16.4+"
163 + >
164 + <Profiler id="count" onRender={onRender}>
165 + <div>
166 + <ProfilerChild />
167 + </div>
168 + </Profiler>
169 + </Feature>
170 + );
171 + case 3:
172 + // createContext()
173 + const LocaleContext = React.createContext();
174 + LocaleContext.displayName = 'LocaleContext';
175 + const ThemeContext = React.createContext();
176 + apps.push(
177 + <Feature key="createContext" label="createContext" version="16.3+">
178 + <ThemeContext.Provider value="blue">
179 + <ThemeContext.Consumer>
180 + {theme => <div>theme: {theme}</div>}
181 + </ThemeContext.Consumer>
182 + </ThemeContext.Provider>
183 + <LocaleContext.Provider value="en-US">
184 + <LocaleContext.Consumer>
185 + {locale => <div>locale: {locale}</div>}
186 + </LocaleContext.Consumer>
187 + </LocaleContext.Provider>
188 + </Feature>
189 + );
190 +
191 + // forwardRef()
192 + const AnonymousFunction = React.forwardRef((props, ref) => (
193 + <div ref={ref}>{props.children}</div>
194 + ));
195 + const NamedFunction = React.forwardRef(function named(props, ref) {
196 + return <div ref={ref}>{props.children}</div>;
197 + });
198 + const CustomName = React.forwardRef((props, ref) => (
199 + <div ref={ref}>{props.children}</div>
200 + ));
201 + CustomName.displayName = 'CustomNameForwardRef';
202 + apps.push(
203 + <Feature key="forwardRef" label="forwardRef" version="16.3+">
204 + <AnonymousFunction>AnonymousFunction</AnonymousFunction>
205 + <NamedFunction>NamedFunction</NamedFunction>
206 + <CustomName>CustomName</CustomName>
207 + </Feature>
208 + );
209 +
210 + // StrictMode
211 + class StrictModeChild extends React.Component {
212 + render() {
213 + return 'StrictModeChild';
214 + }
215 + }
216 + apps.push(
217 + <Feature key="StrictMode" label="StrictMode" version="16.3+">
218 + <React.StrictMode>
219 + <StrictModeChild />
220 + </React.StrictMode>
221 + </Feature>
222 + );
223 +
224 + // unstable_AsyncMode (later renamed to unstable_ConcurrentMode, then ConcurrentMode)
225 + const ConcurrentMode =
226 + React.ConcurrentMode ||
227 + React.unstable_ConcurrentMode ||
228 + React.unstable_AsyncMode;
229 + apps.push(
230 + <Feature
231 + key="AsyncMode/ConcurrentMode"
232 + label="AsyncMode/ConcurrentMode"
233 + version="16.3+"
234 + >
235 + <ConcurrentMode>
236 + <div>
237 + unstable_AsyncMode was added in 16.3, renamed to
238 + unstable_ConcurrentMode in 16.5, and then renamed to
239 + ConcurrentMode in 16.7
240 + </div>
241 + </ConcurrentMode>
242 + </Feature>
243 + );
244 + case 2:
245 + // Fragment
246 + apps.push(
247 + <Feature key="Fragment" label="Fragment" version="16.4+">
248 + <React.Fragment>
249 + <div>one</div>
250 + <div>two</div>
251 + </React.Fragment>
252 + </Feature>
253 + );
254 + case 1:
255 + case 0:
256 + default:
257 + break;
258 + }
259 + break;
260 + case 15:
261 + break;
262 + case 14:
263 + break;
264 + default:
265 + break;
266 +}
267 +
268 +function Even() {
269 + return <small>(even)</small>;
270 +}
271 +
272 +// Simple stateful app shared by all React versions
273 +class SimpleApp extends React.Component {
274 + state = { count: 0 };
275 + incrementCount = () => {
276 + const updaterFn = prevState => ({ count: prevState.count + 1 });
277 + trace('Updating count', performance.now(), () => this.setState(updaterFn));
278 + };
279 + render() {
280 + const { count } = this.state;
281 + return (
282 + <div>
283 + {count % 2 === 0 ? (
284 + <span>
285 + count: {count} <Even />
286 + </span>
287 + ) : (
288 + <span>count: {count}</span>
289 + )}{' '}
290 + <button onClick={this.incrementCount}>increment</button>
291 + </div>
292 + );
293 + }
294 +}
295 +apps.push(
296 + <Feature key="Simple stateful app" label="Simple stateful app" version="any">
297 + <SimpleApp />
298 + </Feature>
299 +);
300 +
301 +// This component, with the version prop, helps organize DevTools at a glance.
302 +function TopLevelWrapperForDevTools({ version }) {
303 + let header = <h1>React {version}</h1>;
304 + if (version.includes('canary')) {
305 + const commitSha = version.match(/.+canary-(.+)/)[1];
306 + header = (
307 + <h1>
308 + React canary{' '}
309 + <a href={`https://github.com/facebook/react/commit/${commitSha}`}>
310 + {commitSha}
311 + </a>
312 + </h1>
313 + );
314 + } else if (version.includes('alpha')) {
315 + header = <h1>React next</h1>;
316 + }
317 +
318 + return (
319 + <div>
320 + {header}
321 + {apps}
322 + </div>
323 + );
324 +}
325 +TopLevelWrapperForDevTools.displayName = 'React';
326 +
327 +ReactDOM.render(
328 + <TopLevelWrapperForDevTools version={React.version} />,
329 + document.getElementById('root')
330 +);
fixtures/regression/styles.css new
+37
@@ -0,0 +1,37 @@
1 +body {
2 + font-family: sans-serif;
3 + font-size: 12px;
4 +}
5 +
6 +h1 {
7 + margin: 0;
8 + font-size: 20px;
9 +}
10 +
11 +h2 {
12 + margin: 1rem 0 0;
13 +}
14 +
15 +iframe {
16 + border: 1px solid #ddd;
17 + border-radius: 0.5rem;
18 +}
19 +
20 +code {
21 + white-space: nowrap;
22 +}
23 +
24 +.Feature {
25 + margin: 1rem 0;
26 + border-bottom: 1px solid #eee;
27 + padding-bottom: 1rem;
28 +}
29 +.FeatureHeader {
30 + font-size: 16px;
31 + margin-bottom: 0.5rem;
32 +}
33 +.FeatureCode {
34 + background-color: #eee;
35 + padding: 0.25rem;
36 + border-radius: 0.25rem;
37 +}
src/backend/agent.js
+2 -2
@@ -113,7 +113,7 @@ export default class Agent extends EventEmitter {
113 const renderer = ((this._rendererInterfaces[
114 (rendererID: any)
115 ]: any): RendererInterface);
116 - return renderer.getFiberIDFromNative(node, true);
116 + return renderer.getInternalIDFromNative(node, true);
117 } catch (e) {}
118 }
119 return null;
@@ -253,7 +253,7 @@ export default class Agent extends EventEmitter {
253
254 let node: HTMLElement | null = null;
255 if (renderer !== null) {
256 - node = ((renderer.findNativeByFiberID(id): any): HTMLElement);
256 + node = ((renderer.getNativeFromInternal(id): any): HTMLElement);
257 }
258
259 if (node != null) {
src/backend/index.js
+6 -1
@@ -4,6 +4,7 @@ import type { DevToolsHook, ReactRenderer, RendererInterface } from './types';
4 import Agent from './agent';
5
6 import { attach } from './renderer';
7 +import { attach as attachLegacy } from './legacy/renderer';
8
9 export function initBackend(
10 hook: DevToolsHook,
@@ -40,7 +41,11 @@ export function initBackend(
41
42 // Inject any not-yet-injected renderers (if we didn't reload-and-profile)
43 if (!rendererInterface) {
43 - rendererInterface = attach(hook, id, renderer, global);
44 + if (typeof renderer.findFiberByHostInstance === 'function') {
45 + rendererInterface = attach(hook, id, renderer, global);
46 + } else {
47 + rendererInterface = attachLegacy(hook, id, renderer, global);
48 + }
49
50 hook.rendererInterfaces.set(id, rendererInterface);
51 }
src/backend/legacy/getChildren.js new
+63
@@ -0,0 +1,63 @@
1 +// @flow
2 +
3 +export default function getChildren(internalInstance: Object): Array<any> {
4 + // If the parent is a native node without rendered children, but with
5 + // multiple string children, then the `element` that gets passed in here is
6 + // a plain value -- a string or number.
7 + if (internalInstance._renderedComponent) {
8 + return [internalInstance._renderedComponent];
9 + } else if (internalInstance._renderedChildren) {
10 + return childrenToArray(internalInstance._renderedChildren);
11 + } else if (
12 + internalInstance._currentElement &&
13 + internalInstance._currentElement.props
14 + ) {
15 + // DevTools doesn't need to display primative child types,
16 + // So we can filter them out early.
17 + /*
18 + const children = [];
19 +
20 + // This is a native node without rendered children -- meaning the children
21 + // prop is the unfiltered list of children.
22 + // This may include 'null' or even other invalid values, so we need to
23 + // filter it the same way that ReactDOM does.
24 + // Instead of pulling in the whole React library, we just copied over the
25 + // 'traverseAllChildrenImpl' method.
26 + // https://github.com/facebook/react/blob/240b84ed8e1db715d759afaae85033718a0b24e1/src/isomorphic/children/ReactChildren.js#L112-L158
27 + const unfilteredChildren = internalInstance._currentElement.props.children;
28 + traverseAllChildrenImpl(
29 + unfilteredChildren,
30 + '', // nameSoFar
31 + (_traverseContext, child) => {
32 + const childType = typeof child;
33 + if (childType === 'string' || childType === 'number') {
34 + children.push(child);
35 + }
36 + }
37 + // traverseContext
38 + );
39 +
40 + return children;
41 + */
42 + }
43 +
44 + if (internalInstance._instance) {
45 + var inst = internalInstance._instance;
46 + // TODO: React ART currently falls in this bucket, but this doesn't
47 + // actually make sense and we should clean this up after stabilizing our
48 + // API for backends
49 + if (inst._renderedChildren) {
50 + return childrenToArray(inst._renderedChildren);
51 + }
52 + }
53 +
54 + return [];
55 +}
56 +
57 +function childrenToArray(children) {
58 + const array = [];
59 + for (var name in children) {
60 + array.push(children[name]);
61 + }
62 + return array;
63 +}
src/backend/legacy/getData.js new
+46
@@ -0,0 +1,46 @@
1 +// @flow
2 +
3 +import {
4 + ElementTypeClass,
5 + ElementTypeOtherOrUnknown,
6 +} from 'src/devtools/types';
7 +import { getDisplayName } from 'src/utils';
8 +
9 +import type { InternalInstance } from './renderer';
10 +import type { FiberData } from '../types';
11 +
12 +export default function getData(internalInstance: InternalInstance): FiberData {
13 + let displayName = null;
14 + let key = null;
15 + let type = ElementTypeOtherOrUnknown;
16 +
17 + // != used deliberately here to catch undefined and null
18 + if (internalInstance._currentElement != null) {
19 + if (internalInstance._currentElement.key) {
20 + key = String(internalInstance._currentElement.key);
21 + }
22 +
23 + const elementType = internalInstance._currentElement.type;
24 + if (typeof elementType === 'string') {
25 + // ...
26 + } else if (typeof elementType === 'function') {
27 + // TODO Can we differentiate between function and class component types?
28 + // Dan said _compositeType tells you PureClass, ImpureClass, StatelessFunctional but it was only added in v14
29 + // getPublicInstance() returns null for function components
30 + type = ElementTypeClass;
31 + displayName = getDisplayName(elementType);
32 + } else if (typeof internalInstance._stringText === 'string') {
33 + // ...
34 + } else {
35 + // TODO What kind of case does this cover?
36 + console.log('what is this type?');
37 + displayName = getDisplayName(elementType);
38 + }
39 + }
40 +
41 + return {
42 + displayName,
43 + key,
44 + type,
45 + };
46 +}
src/backend/legacy/getElementType.js new
+22
@@ -0,0 +1,22 @@
1 +// @flow
2 +import {
3 + ElementTypeClass,
4 + ElementTypeOtherOrUnknown,
5 +} from 'src/devtools/types';
6 +
7 +import type { InternalInstance } from './renderer';
8 +import type { ElementType } from 'src/devtools/types';
9 +
10 +export default function getElementType(
11 + internalInstance: InternalInstance
12 +): ElementType {
13 + // != used deliberately here to catch undefined and null
14 + if (internalInstance._currentElement != null) {
15 + const elementType = internalInstance._currentElement.type;
16 + if (typeof elementType === 'function') {
17 + return ElementTypeClass;
18 + }
19 + }
20 +
21 + return ElementTypeOtherOrUnknown;
22 +}
src/backend/legacy/renderer.js new
+787
@@ -0,0 +1,787 @@
1 +// @flow
2 +
3 +import {
4 + ElementTypeClass,
5 + ElementTypeFunction,
6 + ElementTypeRoot,
7 + ElementTypeOtherOrUnknown,
8 +} from 'src/devtools/types';
9 +import { getUID, utfEncodeString, operationsArrayToString } from '../../utils';
10 +import { cleanForBridge, copyWithSet } from '../utils';
11 +import {
12 + __DEBUG__,
13 + TREE_OPERATION_ADD,
14 + TREE_OPERATION_REMOVE,
15 +} from '../../constants';
16 +import getChildren from './getChildren';
17 +import getData from './getData';
18 +import getElementType from './getElementType';
19 +import {
20 + decorateResult,
21 + decorateMany,
22 + forceUpdate,
23 + restoreMany,
24 +} from './utils';
25 +
26 +import type {
27 + DevToolsHook,
28 + GetInternalIDFromNative,
29 + GetNativeFromInternal,
30 + NativeType,
31 + RendererInterface,
32 +} from '../types';
33 +import type { InspectedElement } from 'src/devtools/views/Components/types';
34 +
35 +export type InternalInstance = Object;
36 +type LegacyRenderer = Object;
37 +
38 +export function attach(
39 + hook: DevToolsHook,
40 + rendererID: number,
41 + renderer: LegacyRenderer,
42 + global: Object
43 +): RendererInterface {
44 + const idToInternalInstanceMap: Map<number, InternalInstance> = new Map();
45 + const idToParentIDMap: Map<number, number> = new Map();
46 + const internalInstanceToIDMap: Map<InternalInstance, number> = new Map();
47 + const mountedIDs: Set<number> = new Set();
48 + const pendingMountIDs: Set<number> = new Set();
49 + const pendingUnmountIDs: Set<number> = new Set();
50 + const rootIDs: Set<number> = new Set();
51 +
52 + function getID(internalInstance: InternalInstance): number {
53 + if (!internalInstanceToIDMap.has(internalInstance)) {
54 + const id = getUID();
55 + internalInstanceToIDMap.set(internalInstance, id);
56 + idToInternalInstanceMap.set(id, internalInstance);
57 + }
58 + return ((internalInstanceToIDMap.get(internalInstance): any): number);
59 + }
60 +
61 + function getChildIDs(internalInstance: InternalInstance): Array<number> {
62 + return getChildren(internalInstance).map(getID);
63 + }
64 +
65 + //function getParentID(internalInstance: InternalInstance): number {
66 + // return getID(internalInstance._hostParent);
67 + //}
68 +
69 + function findNearestAncestorInTree(
70 + internalInstance: InternalInstance
71 + ): number | null {
72 + let current = internalInstance;
73 + while (current != null) {
74 + const id = getID(current);
75 + if (
76 + rootIDs.has(id) ||
77 + getElementType(current) !== ElementTypeOtherOrUnknown
78 + ) {
79 + return id;
80 + }
81 + const parentID = idToParentIDMap.get(id);
82 + current = parentID != null ? idToInternalInstanceMap.get(parentID) : null;
83 + }
84 + return null;
85 + }
86 +
87 + let getInternalIDFromNative: GetInternalIDFromNative = ((null: any): GetInternalIDFromNative);
88 + let getNativeFromInternal: (
89 + id: number
90 + ) => ?NativeType = ((null: any): GetNativeFromInternal);
91 +
92 + // React Native
93 + if (renderer.Mount.findNodeHandle && renderer.Mount.nativeTagToRootNodeID) {
94 + getInternalIDFromNative = (nativeTag, findNearestUnfilteredAncestor) => {
95 + const internalInstance = renderer.Mount.nativeTagToRootNodeID(nativeTag);
96 + return findNearestAncestorInTree(internalInstance);
97 + };
98 + getNativeFromInternal = (id: number) => {
99 + const internalInstance = idToInternalInstanceMap.get(id);
100 + return renderer.Mount.findNodeHandle(internalInstance);
101 + };
102 +
103 + // React DOM 15+
104 + } else if (renderer.ComponentTree) {
105 + getInternalIDFromNative = (node, findNearestUnfilteredAncestor) => {
106 + const internalInstance = renderer.ComponentTree.getClosestInstanceFromNode(
107 + node
108 + );
109 + return findNearestAncestorInTree(internalInstance);
110 + };
111 + getNativeFromInternal = (id: number) => {
112 + const internalInstance = idToInternalInstanceMap.get(id);
113 + return renderer.ComponentTree.getNodeFromInstance(internalInstance);
114 + };
115 +
116 + // React DOM
117 + } else if (renderer.Mount.getID && renderer.Mount.getNode) {
118 + getInternalIDFromNative = (node, findNearestUnfilteredAncestor) => {
119 + let id = renderer.Mount.getID(node);
120 + while (node && node.parentNode && !id) {
121 + node = node.parentNode;
122 + id = renderer.Mount.getID(node);
123 + }
124 + return id;
125 + };
126 +
127 + getNativeFromInternal = (id: number) => {
128 + try {
129 + const internalInstance = idToInternalInstanceMap.get(id);
130 + if (internalInstance != null) {
131 + return renderer.Mount.getNode(internalInstance._rootNodeID);
132 + }
133 + } catch (e) {}
134 +
135 + return null;
136 + };
137 + } else {
138 + console.warn(
139 + 'Unknown React version (does not have getID), probably an unshimmed React Native'
140 + );
141 + }
142 +
143 + let oldReconcilerMethods = null;
144 + let oldRenderComponent = null;
145 + let oldRenderRoot = null;
146 +
147 + // React DOM
148 + if (renderer.Mount._renderNewRootComponent) {
149 + oldRenderRoot = decorateResult(
150 + renderer.Mount,
151 + '_renderNewRootComponent',
152 + internalInstance => {
153 + const id = getID(internalInstance);
154 +
155 + rootIDs.add(id);
156 +
157 + if (__DEBUG__) {
158 + console.log('renderer.Mount._renderNewRootComponent()', id);
159 + }
160 +
161 + recordPendingMount(internalInstance);
162 +
163 + // If we're mounting a root, we've just finished a batch of work,
164 + // so it's safe to synchronously flush.
165 + flushPendingEvents(id);
166 + }
167 + );
168 +
169 + // React Native
170 + } else if (renderer.Mount.renderComponent) {
171 + oldRenderComponent = decorateResult(
172 + renderer.Mount,
173 + 'renderComponent',
174 + internalInstance => {
175 + const id = getID(internalInstance);
176 +
177 + rootIDs.add(id);
178 +
179 + if (__DEBUG__) {
180 + console.log('renderer.Mount.renderComponent()', id);
181 + }
182 +
183 + recordPendingMount(internalInstance);
184 +
185 + // If we're mounting a root, we've just finished a batch of work,
186 + // so it's safe to synchronously flush.
187 + flushPendingEvents(id);
188 + }
189 + );
190 + }
191 +
192 + if (renderer.Reconciler) {
193 + oldReconcilerMethods = decorateMany(renderer.Reconciler, {
194 + mountComponent(internalInstance, rootID, transaction, context) {
195 + recordPendingMount(internalInstance);
196 + },
197 + performUpdateIfNecessary(
198 + internalInstance,
199 + nextChild,
200 + transaction,
201 + context
202 + ) {
203 + // TODO Check for change in order of children
204 + },
205 + receiveComponent(internalInstance, nextChild, transaction, context) {
206 + // TODO Check for change in order of children
207 + },
208 + unmountComponent(internalInstance) {
209 + recordPendingUnmount(internalInstance);
210 + },
211 + });
212 + }
213 +
214 + function cleanup() {
215 + if (oldReconcilerMethods !== null) {
216 + if (renderer.Component) {
217 + restoreMany(renderer.Component.Mixin, oldReconcilerMethods);
218 + } else {
219 + restoreMany(renderer.Reconciler, oldReconcilerMethods);
220 + }
221 + }
222 + if (oldRenderRoot !== null) {
223 + renderer.Mount._renderNewRootComponent = oldRenderRoot;
224 + }
225 + if (oldRenderComponent !== null) {
226 + renderer.Mount.renderComponent = oldRenderComponent;
227 + }
228 + oldReconcilerMethods = null;
229 + oldRenderRoot = null;
230 + oldRenderComponent = null;
231 + }
232 +
233 + let pendingOperations: Uint32Array = new Uint32Array(0);
234 +
235 + function addOperation(
236 + newAction: Uint32Array,
237 + addToStartOfQueue: boolean = false
238 + ): void {
239 + const oldActions = pendingOperations;
240 + pendingOperations = new Uint32Array(oldActions.length + newAction.length);
241 + if (addToStartOfQueue) {
242 + pendingOperations.set(newAction);
243 + pendingOperations.set(oldActions, newAction.length);
244 + } else {
245 + pendingOperations.set(oldActions);
246 + pendingOperations.set(newAction, oldActions.length);
247 + }
248 + }
249 +
250 + // TODO Rethink the below queueing mechanism.
251 + // Every mount is some parent's update (except for the root mount which we can explicitly handle)
252 + // So maybe we only need to call queueFlushPendingEvents() for updates,
253 + // and maybe we can rely on an id-to-root Map for this case, to limit the scope of what we crawl.
254 +
255 + // Older React renderers did not have the concept of a commit.
256 + // The data structure was just ad-hoc mutated in place.
257 + // So except for the case of the root mounting the first time,
258 + // there is no event we can observe to signal that a render is finished.
259 + // However since older renderers were always synchronous,
260 + // we can use setTimeout to batch operations together.
261 + // In the case of a cascading update, we might batch multiple "commits"-
262 + // but that should be okay, since the batching is not strictly necessary.
263 + let flushPendingEventsTimeoutID: TimeoutID | null = null;
264 + function queueFlushPendingEvents() {
265 + if (flushPendingEventsTimeoutID === null) {
266 + flushPendingEventsTimeoutID = setTimeout(() => {
267 + flushPendingEventsTimeoutID = null;
268 +
269 + // If there are pending operations, walk the tree and find them.
270 + // Ideally we wouldjust pluck the pending operations out of the sets directly,
271 + // but without doing a full traversal, it would be hard for us to determine the filtered parent.
272 + // It should be possible to improve this though, by maintaining a map of id-to-parent,
273 + // and crawling upward to the first non-filtered node.
274 + // TODO Revisit this and think about it more...
275 + if (pendingMountIDs.size > 0 || pendingUnmountIDs.size > 0) {
276 + rootIDs.forEach(flushPendingEvents);
277 + }
278 + }, 0);
279 + }
280 + }
281 +
282 + function flushInitialOperations() {
283 + // Older versions of React do not support profiling mode, so there's nothing to flush.
284 + // Crawl roots though and register any nodes that mounted before we were injected.
285 +
286 + const roots =
287 + renderer.Mount._instancesByReactRootID ||
288 + renderer.Mount._instancesByContainerID;
289 +
290 + for (let key in roots) {
291 + const internalInstance = roots[key];
292 + const id = getID(internalInstance);
293 +
294 + rootIDs.add(id);
295 +
296 + crawlAndRecordMounts(id, 0, true);
297 +
298 + // It's safe to synchronously flush for the root we just crawled.
299 + flushPendingEvents(id);
300 + }
301 + }
302 +
303 + function crawlAndRecordMounts(
304 + id: number,
305 + parentID: number,
306 + isInitialMount: boolean
307 + ) {
308 + const internalInstance = idToInternalInstanceMap.get(id);
309 + const shouldIncludeInTree =
310 + parentID === 0 ||
311 + getElementType(internalInstance) !== ElementTypeOtherOrUnknown;
312 +
313 + // Not all nodes are mounted in the frontend DevTools tree,
314 + // but it's important to track parent info even for the unmounted ones.
315 + idToParentIDMap.set(id, parentID);
316 +
317 + if (__DEBUG__) {
318 + console.group(
319 + 'crawlAndRecordMounts() id:',
320 + id,
321 + 'shouldIncludeInTree?',
322 + shouldIncludeInTree
323 + );
324 + }
325 +
326 + if (shouldIncludeInTree) {
327 + const didMount = isInitialMount || pendingMountIDs.has(id);
328 + const didUnmount = pendingUnmountIDs.has(id);
329 +
330 + // If this node was both mounted and unmounted in the same batch,
331 + // just skip it and don't send any update.
332 + if (didMount && didUnmount) {
333 + pendingUnmountIDs.delete(id);
334 + return;
335 + } else if (didMount) {
336 + recordMount(id, parentID);
337 + }
338 + }
339 +
340 + getChildIDs(internalInstance).forEach(childID =>
341 + crawlAndRecordMounts(
342 + childID,
343 + shouldIncludeInTree ? id : parentID,
344 + isInitialMount
345 + )
346 + );
347 +
348 + if (__DEBUG__) {
349 + console.groupEnd();
350 + }
351 + }
352 +
353 + function flushPendingEvents(rootID: number): void {
354 + // Crawl tree and queue mounts/updates.
355 + crawlAndRecordMounts(rootID, 0, false);
356 +
357 + // Send pending deletions.
358 + pendingUnmountIDs.forEach(id => {
359 + if (mountedIDs.has(id)) {
360 + recordUnmount(id);
361 + }
362 + });
363 +
364 + // Identify which renderer this update is coming from.
365 + // This enables roots to be mapped to renderers,
366 + // Which in turn enables fiber props, states, and hooks to be inspected.
367 + const idArray = new Uint32Array(2);
368 + idArray[0] = rendererID;
369 + idArray[1] = rootID;
370 + addOperation(idArray, true);
371 +
372 + if (__DEBUG__) {
373 + operationsArrayToString(pendingOperations);
374 + }
375 +
376 + // If we've already connected to the frontend, just pass the operations through.
377 + hook.emit('operations', pendingOperations);
378 +
379 + pendingMountIDs.clear();
380 + pendingUnmountIDs.clear();
381 + pendingOperations = new Uint32Array(0);
382 + }
383 +
384 + function inspectElement(id: number): InspectedElement | null {
385 + let result = inspectElementRaw(id);
386 + if (result === null) {
387 + return null;
388 + }
389 + // TODO Review sanitization approach for the below inspectable values.
390 + result.context = cleanForBridge(result.context);
391 + result.props = cleanForBridge(result.props);
392 + result.state = cleanForBridge(result.state);
393 + return result;
394 + }
395 +
396 + function inspectElementRaw(id: number): InspectedElement | null {
397 + const internalInstance = idToInternalInstanceMap.get(id);
398 + const data = getData(internalInstance);
399 +
400 + let context = null;
401 + let owners = null;
402 + let props = null;
403 + let state = null;
404 + let source = null;
405 +
406 + if (internalInstance != null) {
407 + const element = internalInstance._currentElement;
408 + if (element !== null) {
409 + props = element.props;
410 + source = element._source != null ? element._source : null;
411 +
412 + let owner = element._owner;
413 + if (owner) {
414 + owners = [];
415 + while (owner != null) {
416 + owners.push({
417 + displayName: getData(owner).displayName || 'Unknown',
418 + id: getID(owner),
419 + });
420 + owner = owner.owner;
421 + }
422 + }
423 + }
424 + context = internalInstance.context || null;
425 + state = internalInstance.state || null;
426 + }
427 +
428 + return {
429 + id,
430 +
431 + // Hooks did not exist in legacy versions
432 + canEditHooks: false,
433 +
434 + // Does the current renderer support editable function props?
435 + canEditFunctionProps: true,
436 +
437 + // Suspense did not exist in legacy versions
438 + canToggleSuspense: false,
439 +
440 + // Can view component source location.
441 + canViewSource:
442 + data.type === ElementTypeClass || data.type === ElementTypeFunction,
443 +
444 + displayName: data.displayName,
445 +
446 + // Inspectable properties.
447 + context,
448 + hooks: null,
449 + props,
450 + state,
451 +
452 + // List of owners
453 + owners,
454 +
455 + // Location of component in source coude.
456 + source,
457 + };
458 + }
459 +
460 + function logElementToConsole(id: number): void {
461 + const result = inspectElementRaw(id);
462 + if (result === null) {
463 + console.warn(`Could not find element with id "${id}"`);
464 + return;
465 + }
466 +
467 + const supportsGroup = typeof console.groupCollapsed === 'function';
468 + if (supportsGroup) {
469 + console.groupCollapsed(
470 + `[Click to expand] %c<${result.displayName || 'Component'} />`,
471 + // --dom-tag-name-color is the CSS variable Chrome styles HTML elements with in the console.
472 + 'color: var(--dom-tag-name-color); font-weight: normal;'
473 + );
474 + }
475 + if (result.props !== null) {
476 + console.log('Props:', result.props);
477 + }
478 + if (result.state !== null) {
479 + console.log('State:', result.state);
480 + }
481 + if (result.context !== null) {
482 + console.log('State:', result.context);
483 + }
484 + const nativeNode = getNativeFromInternal(id);
485 + if (nativeNode !== null) {
486 + console.log('Node:', nativeNode);
487 + }
488 + if (window.chrome || /firefox/i.test(navigator.userAgent)) {
489 + console.log(
490 + 'Right-click any value to save it as a global variable for further inspection.'
491 + );
492 + }
493 + if (supportsGroup) {
494 + console.groupEnd();
495 + }
496 + }
497 +
498 + function prepareViewElementSource(id: number): void {
499 + const internalInstance = idToInternalInstanceMap.get(id);
500 + if (internalInstance == null) {
501 + console.warn(`Could not find instance with id "${id}"`);
502 + return;
503 + }
504 +
505 + const element = internalInstance._currentElement;
506 + if (element == null) {
507 + console.warn(`Could not find element with id "${id}"`);
508 + return;
509 + }
510 +
511 + global.$type = element.type;
512 + }
513 +
514 + function selectElement(id: number): void {
515 + const internalInstance = idToInternalInstanceMap.get(id);
516 + if (internalInstance == null) {
517 + console.warn(`Could not find instance with id "${id}"`);
518 + return;
519 + }
520 +
521 + switch (getElementType(internalInstance)) {
522 + case ElementTypeClass:
523 + case ElementTypeFunction:
524 + const element = internalInstance._currentElement;
525 + if (element == null) {
526 + console.warn(`Could not find element with id "${id}"`);
527 + return;
528 + }
529 +
530 + global.$r = {
531 + props: element.props,
532 + type: element.type,
533 + };
534 + break;
535 + default:
536 + break;
537 + }
538 + }
539 +
540 + function recordPendingMount(internalInstance: InternalInstance) {
541 + pendingMountIDs.add(getID(internalInstance));
542 +
543 + if (__DEBUG__) {
544 + console.log(
545 + '%crecordPendingMount()',
546 + 'color: green',
547 + getID(internalInstance)
548 + );
549 + }
550 +
551 + queueFlushPendingEvents();
552 + }
553 +
554 + function recordPendingUnmount(internalInstance: InternalInstance) {
555 + const id = getID(internalInstance);
556 +
557 + pendingUnmountIDs.add(id);
558 +
559 + // Not all nodes are mounted (or unmounted) in the frontend DevTools tree,
560 + // so it's important to remove entries from this map on pending unmount.
561 + idToParentIDMap.delete(id);
562 +
563 + if (__DEBUG__) {
564 + console.log(
565 + '%crecordPendingUnmount()',
566 + 'color: red',
567 + getID(internalInstance)
568 + );
569 + }
570 +
571 + queueFlushPendingEvents();
572 + }
573 +
574 + function recordMount(id: number, parentID: number) {
575 + const internalInstance = ((idToInternalInstanceMap.get(
576 + id
577 + ): any): InternalInstance);
578 + const isRoot = rootIDs.has(id);
579 +
580 + if (__DEBUG__) {
581 + console.log(
582 + '%crecordMount()',
583 + 'color: green; font-weight: bold;',
584 + id,
585 + getData(internalInstance).displayName
586 + );
587 + }
588 +
589 + mountedIDs.add(id);
590 +
591 + if (isRoot) {
592 + // TODO Is this right? For all versions?
593 + const hasOwnerMetadata =
594 + internalInstance._currentElement != null &&
595 + internalInstance._currentElement._owner != null;
596 +
597 + const operation = new Uint32Array(5);
598 + operation[0] = TREE_OPERATION_ADD;
599 + operation[1] = id;
600 + operation[2] = ElementTypeRoot;
601 + operation[3] = 0; // isProfilingSupported?
602 + operation[4] = hasOwnerMetadata ? 1 : 0;
603 + addOperation(operation);
604 + } else {
605 + const { displayName, key, type } = getData(internalInstance);
606 +
607 + const ownerID =
608 + internalInstance._currentElement != null &&
609 + internalInstance._currentElement._owner != null
610 + ? getID(internalInstance._currentElement._owner)
611 + : 0;
612 +
613 + let encodedDisplayName = ((null: any): Uint8Array);
614 + let encodedKey = ((null: any): Uint8Array);
615 +
616 + if (displayName !== null) {
617 + encodedDisplayName = utfEncodeString(displayName);
618 + }
619 +
620 + if (key !== null) {
621 + // React$Key supports string and number types as inputs,
622 + // But React converts numeric keys to strings, so we only have to handle that type here.
623 + // https://github.com/facebook/react/blob/0e67969cb1ad8c27a72294662e68fa5d7c2c9783/packages/react/src/ReactElement.js#L187
624 + encodedKey = utfEncodeString(((key: any): string));
625 + }
626 +
627 + const encodedDisplayNameSize =
628 + displayName === null ? 0 : encodedDisplayName.length;
629 + const encodedKeySize = key === null ? 0 : encodedKey.length;
630 +
631 + const operation = new Uint32Array(
632 + 7 + encodedDisplayNameSize + encodedKeySize
633 + );
634 + operation[0] = TREE_OPERATION_ADD;
635 + operation[1] = id;
636 + operation[2] = type;
637 + operation[3] = parentID;
638 + operation[4] = ownerID;
639 + operation[5] = encodedDisplayNameSize;
640 + if (displayName !== null) {
641 + operation.set(encodedDisplayName, 6);
642 + }
643 + operation[6 + encodedDisplayNameSize] = encodedKeySize;
644 + if (key !== null) {
645 + operation.set(encodedKey, 6 + encodedDisplayNameSize + 1);
646 + }
647 + addOperation(operation);
648 + }
649 + }
650 +
651 + function recordUnmount(id: number) {
652 + const internalInstance = idToInternalInstanceMap.get(id);
653 + const isRoot = rootIDs.has(id);
654 +
655 + if (__DEBUG__) {
656 + console.log(
657 + '%crecordUnmount()',
658 + 'color: red; font-weight: bold;',
659 + id,
660 + getData(internalInstance).displayName
661 + );
662 + }
663 +
664 + if (isRoot) {
665 + const operation = new Uint32Array(2);
666 + operation[0] = TREE_OPERATION_REMOVE;
667 + operation[1] = id;
668 + addOperation(operation);
669 +
670 + rootIDs.delete(id);
671 + } else {
672 + const operation = new Uint32Array(2);
673 + operation[0] = TREE_OPERATION_REMOVE;
674 + operation[1] = id;
675 + addOperation(operation);
676 + }
677 +
678 + idToInternalInstanceMap.delete(id);
679 + internalInstanceToIDMap.delete(internalInstance);
680 +
681 + mountedIDs.delete(id);
682 + }
683 +
684 + function setInProps(id: number, path: Array<string | number>, value: any) {
685 + const internalInstance = idToInternalInstanceMap.get(id);
686 + if (internalInstance != null) {
687 + const element = internalInstance._currentElement;
688 + internalInstance._currentElement = {
689 + ...element,
690 + props: copyWithSet(element.props, path, value),
691 + };
692 + forceUpdate(internalInstance._instance);
693 + }
694 + }
695 +
696 + function setInState(id: number, path: Array<string | number>, value: any) {
697 + const internalInstance = idToInternalInstanceMap.get(id);
698 + if (internalInstance != null) {
699 + setIn(internalInstance.state, path, value);
700 + internalInstance.forceUpdate();
701 + }
702 + }
703 +
704 + function setInContext(id: number, path: Array<string | number>, value: any) {
705 + const internalInstance = idToInternalInstanceMap.get(id);
706 + if (internalInstance != null) {
707 + setIn(internalInstance.context, path, value);
708 + forceUpdate(internalInstance);
709 + }
710 + }
711 +
712 + function setIn(obj: Object, path: Array<string | number>, value: any) {
713 + const last = path.pop();
714 + const parent = path.reduce(
715 + // $FlowFixMe
716 + (reduced, attr) => (reduced ? reduced[attr] : null),
717 + obj
718 + );
719 + if (parent) {
720 + // $FlowFixMe
721 + parent[last] = value;
722 + }
723 + }
724 +
725 + // v16+ only features
726 + const getCommitDetails = () => {
727 + throw new Error('getCommitDetails not supported by this renderer');
728 + };
729 + const getFiberCommits = () => {
730 + throw new Error('getFiberCommits not supported by this renderer');
731 + };
732 + const getInteractions = () => {
733 + throw new Error('getInteractions not supported by this renderer');
734 + };
735 + const getProfilingDataForDownload = () => {
736 + throw new Error(
737 + 'getProfilingDataForDownload not supported by this renderer'
738 + );
739 + };
740 + const getProfilingSummary = () => {
741 + throw new Error('getProfilingSummary not supported by this renderer');
742 + };
743 + const handleCommitFiberRoot = () => {
744 + throw new Error('handleCommitFiberRoot not supported by this renderer');
745 + };
746 + const handleCommitFiberUnmount = () => {
747 + throw new Error('handleCommitFiberUnmount not supported by this renderer');
748 + };
749 + const overrideSuspense = () => {
750 + throw new Error('overrideSuspense not supported by this renderer');
751 + };
752 + const setInHook = () => {
753 + throw new Error('setInHook not supported by this renderer');
754 + };
755 + const startProfiling = () => {
756 + throw new Error('startProfiling not supported by this renderer');
757 + };
758 + const stopProfiling = () => {
759 + throw new Error('stopProfiling not supported by this renderer');
760 + };
761 +
762 + return {
763 + cleanup,
764 + flushInitialOperations,
765 + getCommitDetails,
766 + getFiberCommits,
767 + getInteractions,
768 + getInternalIDFromNative,
769 + getNativeFromInternal,
770 + getProfilingDataForDownload,
771 + getProfilingSummary,
772 + handleCommitFiberRoot,
773 + handleCommitFiberUnmount,
774 + inspectElement,
775 + logElementToConsole,
776 + overrideSuspense,
777 + prepareViewElementSource,
778 + renderer,
779 + selectElement,
780 + setInContext,
781 + setInHook,
782 + setInProps,
783 + setInState,
784 + startProfiling,
785 + stopProfiling,
786 + };
787 +}
src/backend/legacy/traverseAllChildrenImpl.js new
+146
@@ -0,0 +1,146 @@
1 +// @flow
2 +
3 +const SEPARATOR = '.';
4 +const SUBSEPARATOR = ':';
5 +
6 +const FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
7 +// The Symbol used to tag the ReactElement type. If there is no native Symbol
8 +// nor polyfill, then a plain number is used for performance.
9 +const ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
10 +const REACT_ELEMENT_TYPE =
11 + (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
12 + 0xeac7;
13 +
14 +/**
15 + * Escape and wrap key so it is safe to use as a reactid
16 + *
17 + * @param {string} key to be escaped.
18 + * @return {string} the escaped key.
19 + */
20 +function escape(key: string): string {
21 + const escapeRegex = /[=:]/g;
22 + const escaperLookup = {
23 + '=': '=0',
24 + ':': '=2',
25 + };
26 + const escapedString = ('' + key).replace(escapeRegex, function(match) {
27 + return escaperLookup[match];
28 + });
29 +
30 + return '$' + escapedString;
31 +}
32 +
33 +/**
34 + * Generate a key string that identifies a component within a set.
35 + *
36 + * @param {*} component A component that could contain a manual key.
37 + * @param {number} index Index that is used if a manual key is not provided.
38 + * @return {string}
39 + */
40 +function getComponentKey(component, index) {
41 + // Do some typechecking here since we call this blindly. We want to ensure
42 + // that we don't block potential future ES APIs.
43 + if (
44 + typeof component === 'object' &&
45 + component !== null &&
46 + component.key != null
47 + ) {
48 + // Explicit key
49 + return escape(component.key);
50 + }
51 + // Implicit key determined by the index in the set
52 + return index.toString(36);
53 +}
54 +
55 +/**
56 + * We do a copied the 'traverseAllChildrenImpl' method from
57 + * `React.Children` so that we don't pull in the whole React library.
58 + * @param {?*} children Children tree container.
59 + * @param {!string} nameSoFar Name of the key path so far.
60 + * @param {!function} callback Callback to invoke with each child found.
61 + * @param {?*} traverseContext Used to pass information throughout the traversal
62 + * process.
63 + * @return {!number} The number of children in this subtree.
64 + */
65 +export default function traverseAllChildrenImpl(
66 + children: any,
67 + nameSoFar: string,
68 + callback: Function,
69 + traverseContext: any
70 +): number {
71 + const type = typeof children;
72 +
73 + if (type === 'undefined' || type === 'boolean') {
74 + // All of the above are perceived as null.
75 + children = null;
76 + }
77 +
78 + if (
79 + children === null ||
80 + type === 'string' ||
81 + type === 'number' ||
82 + // The following is inlined from ReactElement. This means we can optimize
83 + // some checks. React Fiber also inlines this logic for similar purposes.
84 + (type === 'object' && children.$$typeof === REACT_ELEMENT_TYPE)
85 + ) {
86 + callback(
87 + traverseContext,
88 + children,
89 + // If it's the only child, treat the name as if it was wrapped in an array
90 + // so that it's consistent if the number of children grows.
91 + nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar
92 + );
93 + return 1;
94 + }
95 +
96 + let child;
97 + let nextName;
98 + let subtreeCount = 0; // Count of children found in the current subtree.
99 + let nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
100 +
101 + if (Array.isArray(children)) {
102 + for (let i = 0; i < children.length; i++) {
103 + child = children[i];
104 + nextName = nextNamePrefix + getComponentKey(child, i);
105 + subtreeCount += traverseAllChildrenImpl(
106 + child,
107 + nextName,
108 + callback,
109 + traverseContext
110 + );
111 + }
112 + } else {
113 + let iteratorFn =
114 + (ITERATOR_SYMBOL && children[ITERATOR_SYMBOL]) ||
115 + children[FAUX_ITERATOR_SYMBOL];
116 + if (typeof iteratorFn === 'function') {
117 + let iterator = iteratorFn.call(children);
118 + let step;
119 + let ii = 0;
120 + while (!(step = iterator.next()).done) {
121 + child = step.value;
122 + nextName = nextNamePrefix + getComponentKey(child, ii++);
123 + subtreeCount += traverseAllChildrenImpl(
124 + child,
125 + nextName,
126 + callback,
127 + traverseContext
128 + );
129 + }
130 + } else if (type === 'object') {
131 + let addendum =
132 + ' If you meant to render a collection of children, use an array ' +
133 + 'instead.';
134 + let childrenString = '' + children;
135 + throw Error(
136 + `The React Devtools cannot render an object as a child. (found: ${
137 + childrenString === '[object Object]'
138 + ? 'object with keys {' + Object.keys(children).join(', ') + '}'
139 + : childrenString
140 + }). ${addendum}`
141 + );
142 + }
143 + }
144 +
145 + return subtreeCount;
146 +}
src/backend/legacy/utils.js new
+55
@@ -0,0 +1,55 @@
1 +// @flow
2 +
3 +import type { InternalInstance } from './renderer';
4 +
5 +export function decorateResult(
6 + object: Object,
7 + attr: string,
8 + fn: Function
9 +): Function {
10 + const old = object[attr];
11 + object[attr] = function(instance: InternalInstance) {
12 + const res = old.apply(this, arguments);
13 + fn(res);
14 + return res;
15 + };
16 + return old;
17 +}
18 +
19 +export function decorate(object: Object, attr: string, fn: Function): Function {
20 + const old = object[attr];
21 + object[attr] = function(instance: InternalInstance) {
22 + const res = old.apply(this, arguments);
23 + fn.apply(this, arguments);
24 + return res;
25 + };
26 + return old;
27 +}
28 +
29 +export function decorateMany(
30 + source: Object,
31 + fns: { [attr: string]: Function }
32 +): Object {
33 + const olds = {};
34 + for (const name in fns) {
35 + olds[name] = decorate(source, name, fns[name]);
36 + }
37 + return olds;
38 +}
39 +
40 +export function restoreMany(source: Object, olds: Object): void {
41 + for (let name in olds) {
42 + source[name] = olds[name];
43 + }
44 +}
45 +
46 +export function forceUpdate(instance: InternalInstance): void {
47 + if (typeof instance.forceUpdate === 'function') {
48 + instance.forceUpdate();
49 + } else if (
50 + instance.updater != null &&
51 + typeof instance.updater.enqueueForceUpdate === 'function'
52 + ) {
53 + instance.updater.enqueueForceUpdate(this, () => {}, 'forceUpdate');
54 + }
55 +}
src/backend/renderer.js
+17 -10
@@ -14,7 +14,7 @@ import {
14 ElementTypeRoot,
15 ElementTypeSuspense,
16 } from 'src/devtools/types';
17 -import { getDisplayName, utfEncodeString } from '../utils';
17 +import { getDisplayName, getUID, utfEncodeString } from '../utils';
18 import { cleanForBridge, copyWithSet, setInObject } from './utils';
19 import {
20 __DEBUG__,
@@ -25,7 +25,6 @@ import {
25 TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN,
26 TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
27 } from '../constants';
28 -import { getUID } from '../utils';
28 import { inspectHooksOfFiber } from './ReactDebugHooks';
29
30 import type {
@@ -312,8 +311,6 @@ export function attach(
311 : symbolOrNumber;
312 }
313
315 - // TODO: we might want to change the data structure once we no longer suppport Stack versions of `getData`.
316 - // TODO: Keep in sync with getElementType()
314 function getDataForFiber(fiber: Fiber): FiberData {
315 const { elementType, type, key, tag } = fiber;
316
@@ -1166,7 +1163,7 @@ export function attach(
1163 currentRootID = -1;
1164 }
1165
1169 - function findNativeByFiberID(id: number) {
1166 + function getNativeFromInternal(id: number) {
1167 try {
1168 const fiber = findCurrentFiberUsingSlowPath(idToFiberMap.get(id));
1169 if (fiber === null) {
@@ -1190,7 +1187,7 @@ export function attach(
1187 }
1188 }
1189
1193 - function getFiberIDFromNative(
1190 + function getInternalIDFromNative(
1191 hostInstance,
1192 findNearestUnfilteredAncestor = false
1193 ) {
@@ -1441,6 +1438,14 @@ export function attach(
1438 case ForwardRef:
1439 global.$type = fiber.type.render;
1440 break;
1441 + case MemoComponent:
1442 + case SimpleMemoComponent:
1443 + const { elementType, type } = fiber;
1444 + global.$type =
1445 + elementType != null && elementType.type != null
1446 + ? elementType.type
1447 + : type;
1448 + break;
1449 default:
1450 global.$type = null;
1451 break;
@@ -1483,7 +1488,9 @@ export function attach(
1488 tag === FunctionComponent ||
1489 tag === IncompleteClassComponent ||
1490 tag === IndeterminateComponent ||
1486 - tag === ForwardRef
1491 + tag === MemoComponent ||
1492 + tag === ForwardRef ||
1493 + tag === SimpleMemoComponent
1494 ) {
1495 canViewSource = true;
1496 if (stateNode && stateNode.context != null) {
@@ -1623,7 +1630,7 @@ export function attach(
1630 if (result.hooks !== null) {
1631 console.log('Hooks:', result.hooks);
1632 }
1626 - const nativeNode = findNativeByFiberID(id);
1633 + const nativeNode = getNativeFromInternal(id);
1634 if (nativeNode !== null) {
1635 console.log('Node:', nativeNode);
1636 }
@@ -1938,10 +1945,10 @@ export function attach(
1945 cleanup,
1946 flushInitialOperations,
1947 getCommitDetails,
1941 - getFiberIDFromNative,
1948 + getInternalIDFromNative,
1949 getFiberCommits,
1950 getInteractions,
1944 - findNativeByFiberID,
1951 + getNativeFromInternal,
1952 getProfilingDataForDownload,
1953 getProfilingSummary,
1954 handleCommitFiberRoot,
src/backend/types.js
+11 -8
@@ -19,7 +19,7 @@ export type FiberData = {|
19 type: ElementType,
20 |};
21
22 -export type NativeType = {};
22 +export type NativeType = Object;
23 export type RendererID = number;
24
25 type Dispatcher = any;
@@ -90,17 +90,20 @@ export type ProfilingSummary = {|
90 rootID: number,
91 |};
92
93 +export type GetInternalIDFromNative = (
94 + component: NativeType,
95 + findNearestUnfilteredAncestor?: boolean
96 +) => number | null;
97 +export type GetNativeFromInternal = (id: number) => ?NativeType;
98 +
99 export type RendererInterface = {
100 cleanup: () => void,
95 - findNativeByFiberID: (id: number) => ?NativeType,
101 flushInitialOperations: () => void,
102 getCommitDetails: (rootID: number, commitIndex: number) => CommitDetails,
98 - getFiberIDFromNative: (
99 - component: NativeType,
100 - findNearestUnfilteredAncestor?: boolean
101 - ) => number | null,
103 getFiberCommits: (rootID: number, fiberID: number) => FiberCommits,
104 getInteractions: (rootID: number) => Interactions,
105 + getInternalIDFromNative: GetInternalIDFromNative,
106 + getNativeFromInternal: GetNativeFromInternal,
107 getProfilingDataForDownload: (rootID: number) => Object,
108 getProfilingSummary: (rootID: number) => ProfilingSummary,
109 handleCommitFiberRoot: (fiber: Object) => void,
@@ -112,14 +115,14 @@ export type RendererInterface = {
115 renderer: ReactRenderer | null,
116 selectElement: (id: number) => void,
117 setInContext: (id: number, path: Array<string | number>, value: any) => void,
118 + setInProps: (id: number, path: Array<string | number>, value: any) => void,
119 + setInState: (id: number, path: Array<string | number>, value: any) => void,
120 setInHook: (
121 id: number,
122 index: number,
123 path: Array<string | number>,
124 value: any
125 ) => void,
121 - setInProps: (id: number, path: Array<string | number>, value: any) => void,
122 - setInState: (id: number, path: Array<string | number>, value: any) => void,
126 startProfiling: () => void,
127 stopProfiling: () => void,
128 };
src/utils.js
+104 -1
@@ -1,6 +1,16 @@
1 // @flow
2
3 -const LRU = require('lru-cache');
3 +import LRU from 'lru-cache';
4 +import {
5 + TREE_OPERATION_ADD,
6 + TREE_OPERATION_REMOVE,
7 + TREE_OPERATION_RESET_CHILDREN,
8 + TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN,
9 + TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
10 +} from './constants';
11 +import { ElementTypeRoot } from 'src/devtools/types';
12 +
13 +import type { ElementType } from 'src/devtools/types';
14
15 const FB_MODULE_RE = /^(.*) \[from (.*)\]$/;
16 const cachedDisplayNames: WeakMap<Function, string> = new WeakMap();
@@ -76,3 +86,96 @@ export function utfEncodeString(string: string): Uint32Array {
86 function toCodePoint(string: string) {
87 return string.codePointAt(0);
88 }
89 +
90 +export function operationsArrayToString(operations: Uint32Array) {
91 + const rendererID = operations[0];
92 + const rootID = operations[1];
93 +
94 + console.group('rendererID:', rendererID, 'rootID:', rootID);
95 +
96 + let i = 2;
97 + while (i < operations.length) {
98 + let id: number = ((null: any): number);
99 + let parentID: number = ((null: any): number);
100 + let type: ElementType = ((null: any): ElementType);
101 +
102 + const operation = operations[i];
103 +
104 + switch (operation) {
105 + case TREE_OPERATION_ADD:
106 + id = ((operations[i + 1]: any): number);
107 + type = ((operations[i + 2]: any): ElementType);
108 +
109 + i = i + 3;
110 +
111 + if (type === ElementTypeRoot) {
112 + console.log(`Add root fiber ${id}`);
113 +
114 + i++; // supportsProfiling
115 + i++; // hasOwnerMetadata
116 + } else {
117 + parentID = ((operations[i]: any): number);
118 + i++;
119 +
120 + i++; // ownerID
121 +
122 + const displayNameLength = operations[i];
123 + i++;
124 + const displayName =
125 + displayNameLength === 0
126 + ? null
127 + : utfDecodeString(
128 + (operations.slice(i, i + displayNameLength): any)
129 + );
130 + i += displayNameLength;
131 +
132 + const keyLength = operations[i];
133 + i++;
134 + i += +keyLength;
135 +
136 + console.log(
137 + `Add fiber ${id} (${displayName || 'null'}) as child of ${parentID}`
138 + );
139 + }
140 + break;
141 + case TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN: {
142 + id = ((operations[i + 1]: any): number);
143 +
144 + i = i + 2;
145 +
146 + console.log(`Recursively remove children from fiber ${id}`);
147 + break;
148 + }
149 + case TREE_OPERATION_REMOVE: {
150 + id = ((operations[i + 1]: any): number);
151 +
152 + i = i + 2;
153 +
154 + console.log(`Remove fiber ${id}`);
155 + break;
156 + }
157 + case TREE_OPERATION_RESET_CHILDREN:
158 + id = ((operations[i + 1]: any): number);
159 + const numChildren = ((operations[i + 2]: any): number);
160 + const children = ((operations.slice(
161 + i + 3,
162 + i + 3 + numChildren
163 + ): any): Array<number>);
164 +
165 + i = i + 3 + numChildren;
166 +
167 + console.log(`Re-order fiber ${id} children ${children.join(',')}`);
168 + break;
169 + case TREE_OPERATION_UPDATE_TREE_BASE_DURATION:
170 + // Base duration updates are only sent while profiling is in progress.
171 + // We can ignore them at this point.
172 + // The profiler UI uses them lazily in order to generate the tree.
173 + i = i + 3;
174 + break;
175 + default:
176 + throw Error(`Unsupported Bridge operation ${operation}`);
177 + }
178 + }
179 +
180 + console.groupEnd();
181 +}