Updated React version check to account for older (<15) versions
Brian Vaughn committed
Jun 12, 2019 at 18:56 UTC
9eb6d3465d2c3e0afc47153890063ef5d939e75e
2 files changed
+83
-2
src/backend/types.js
+3
@@ -116,6 +116,9 @@ export type ReactRenderer = {
116
117
// Only injected by React v16.8+ in order to support hooks inspection.
118
currentDispatcherRef?: {| current: null | Dispatcher |},
119
+
120
+ // <= 15
121
+ Mount?: any,
122
};
123
124
export type ChangeDescription = {|
src/hook.js
+80
-2
@@ -17,6 +17,7 @@ export function installHook(target: any): DevToolsHook | null {
17
function detectReactBuildType(renderer) {
18
try {
19
if (typeof renderer.version === 'string') {
20
+ // React DOM Fiber (16+)
21
if (renderer.bundleType > 0) {
22
// This is not a production build.
23
// We are currently only using 0 (PROD) and 1 (DEV)
@@ -27,15 +28,92 @@ export function installHook(target: any): DevToolsHook | null {
28
// React 16 uses flat bundles. If we report the bundle as production
29
// version, it means we also minified and envified it ourselves.
30
return 'production';
30
-
31
// Note: There is still a risk that the CommonJS entry point has not
32
// been envified or uglified. In this case the user would have *both*
33
// development and production bundle, but only the prod one would run.
34
// This would be really bad. We have a separate check for this because
35
// it happens *outside* of the renderer injection. See `checkDCE` below.
36
}
37
- } catch (err) {}
37
38
+ const toString = Function.prototype.toString;
39
+ if (renderer.Mount && renderer.Mount._renderNewRootComponent) {
40
+ // React DOM Stack
41
+ const renderRootCode = toString.call(
42
+ renderer.Mount._renderNewRootComponent
43
+ );
44
+ // Filter out bad results (if that is even possible):
45
+ if (renderRootCode.indexOf('function') !== 0) {
46
+ // Hope for the best if we're not sure.
47
+ return 'production';
48
+ }
49
+ // Check for React DOM Stack < 15.1.0 in development.
50
+ // If it contains "storedMeasure" call, it's wrapped in ReactPerf (DEV only).
51
+ // This would be true even if it's minified, as method name still matches.
52
+ if (renderRootCode.indexOf('storedMeasure') !== -1) {
53
+ return 'development';
54
+ }
55
+ // For other versions (and configurations) it's not so easy.
56
+ // Let's quickly exclude proper production builds.
57
+ // If it contains a warning message, it's either a DEV build,
58
+ // or an PROD build without proper dead code elimination.
59
+ if (renderRootCode.indexOf('should be a pure function') !== -1) {
60
+ // Now how do we tell a DEV build from a bad PROD build?
61
+ // If we see NODE_ENV, we're going to assume this is a dev build
62
+ // because most likely it is referring to an empty shim.
63
+ if (renderRootCode.indexOf('NODE_ENV') !== -1) {
64
+ return 'development';
65
+ }
66
+ // If we see "development", we're dealing with an envified DEV build
67
+ // (such as the official React DEV UMD).
68
+ if (renderRootCode.indexOf('development') !== -1) {
69
+ return 'development';
70
+ }
71
+ // I've seen process.env.NODE_ENV !== 'production' being smartly
72
+ // replaced by `true` in DEV by Webpack. I don't know how that
73
+ // works but we can safely guard against it because `true` was
74
+ // never used in the function source since it was written.
75
+ if (renderRootCode.indexOf('true') !== -1) {
76
+ return 'development';
77
+ }
78
+ // By now either it is a production build that has not been minified,
79
+ // or (worse) this is a minified development build using non-standard
80
+ // environment (e.g. "staging"). We're going to look at whether
81
+ // the function argument name is mangled:
82
+ if (
83
+ // 0.13 to 15
84
+ renderRootCode.indexOf('nextElement') !== -1 ||
85
+ // 0.12
86
+ renderRootCode.indexOf('nextComponent') !== -1
87
+ ) {
88
+ // We can't be certain whether this is a development build or not,
89
+ // but it is definitely unminified.
90
+ return 'unminified';
91
+ } else {
92
+ // This is likely a minified development build.
93
+ return 'development';
94
+ }
95
+ }
96
+ // By now we know that it's envified and dead code elimination worked,
97
+ // but what if it's still not minified? (Is this even possible?)
98
+ // Let's check matches for the first argument name.
99
+ if (
100
+ // 0.13 to 15
101
+ renderRootCode.indexOf('nextElement') !== -1 ||
102
+ // 0.12
103
+ renderRootCode.indexOf('nextComponent') !== -1
104
+ ) {
105
+ return 'unminified';
106
+ }
107
+ // Seems like we're using the production version.
108
+ // However, the branch above is Stack-only so this is 15 or earlier.
109
+ return 'outdated';
110
+ }
111
+ } catch (err) {
112
+ // Weird environments may exist.
113
+ // This code needs a higher fault tolerance
114
+ // because it runs even with closed DevTools.
115
+ // TODO: should we catch errors in all injected code, and not just this part?
116
+ }
117
return 'production';
118
}
119