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
+}