diff --git a/backend/installGlobalHook.js b/backend/installGlobalHook.js index 93973c90f9..8bbb431f26 100644 --- a/backend/installGlobalHook.js +++ b/backend/installGlobalHook.js @@ -22,7 +22,6 @@ function installGlobalHook(window: Object) { } function detectReactBuildType(renderer) { try { - var toString = Function.prototype.toString; if (typeof renderer.version === 'string') { // React DOM Fiber (16+) if (renderer.bundleType > 0) { @@ -31,29 +30,17 @@ function installGlobalHook(window: Object) { // but might add 2 (PROFILE) in the future. return 'development'; } - // The above should cover envification, but we should still make sure - // that the bundle code has been uglified. - var findFiberCode = toString.call(renderer.findFiberByHostInstance); - // Filter out bad results (if that is even possible): - if (findFiberCode.indexOf('function') !== 0) { - // Hope for the best if we're not sure. - return 'production'; - } - - // By now we know that it's envified--but what if it's not minified? - // This can be bad too, as it means DEV code is still there. - - // FIXME: this is fragile! - // We should replace this check with check on a specially passed - // function. This also doesn't detect lack of dead code elimination - // (although this is not a problem since flat bundles). - if (findFiberCode.indexOf('getClosestInstanceFromNode') !== -1) { - return 'unminified'; - } - // We're good. + // React 16 uses flat bundles. If we report the bundle as production + // version, it means we also minified and envified it ourselves. return 'production'; + // Note: There is still a risk that the CommonJS entry point has not + // been envified or uglified. In this case the user would have *both* + // development and production bundle, but only the prod one would run. + // This would be really bad. We have a separate check for this because + // it happens *outside* of the renderer injection. See `checkDCE` below. } + var toString = Function.prototype.toString; if (renderer.Mount && renderer.Mount._renderNewRootComponent) { // React DOM Stack var renderRootCode = toString.call(renderer.Mount._renderNewRootComponent); @@ -141,14 +128,45 @@ function installGlobalHook(window: Object) { } return 'production'; } + + let hasDetectedBadDCE = false; + const hook = ({ // Shared between Stack and Fiber: _renderers: {}, helpers: {}, + checkDCE: function(fn) { + // This runs for production versions of React. + // Needs to be super safe. + try { + var toString = Function.prototype.toString; + var code = toString.call(fn); + // This is a string embedded in the passed function under DEV-only + // condition. However the function executes only in PROD. Therefore, + // if we see it, dead code elimination did not work. + if (code.indexOf('^_^') > -1) { + // Remember to report during next injection. + hasDetectedBadDCE = true; + // Bonus: throw an exception hoping that it gets picked up by + // a reporting system. Not synchronously so that it doesn't break the + // calling code. + setTimeout(function() { + throw new Error( + 'React is running in production mode, but dead code ' + + 'elimination has not been applied. Read how to correctly ' + + 'configure React for production: ' + + 'https://fburl.com/react-perf-use-the-production-build' + ); + }); + } + } catch (err) { } + }, inject: function(renderer) { var id = Math.random().toString(16).slice(2); hook._renderers[id] = renderer; - var reactBuildType = detectReactBuildType(renderer); + var reactBuildType = hasDetectedBadDCE ? + 'deadcode' : + detectReactBuildType(renderer); hook.emit('renderer', {id, renderer, reactBuildType}); return id; }, diff --git a/shells/webextension/icons/128-deadcode.png b/shells/webextension/icons/128-deadcode.png new file mode 100644 index 0000000000..b6ecc88e13 Binary files /dev/null and b/shells/webextension/icons/128-deadcode.png differ diff --git a/shells/webextension/icons/16-deadcode.png b/shells/webextension/icons/16-deadcode.png new file mode 100644 index 0000000000..33d99798e0 Binary files /dev/null and b/shells/webextension/icons/16-deadcode.png differ diff --git a/shells/webextension/icons/32-deadcode.png b/shells/webextension/icons/32-deadcode.png new file mode 100644 index 0000000000..c4a6bda3e6 Binary files /dev/null and b/shells/webextension/icons/32-deadcode.png differ diff --git a/shells/webextension/icons/48-deadcode.png b/shells/webextension/icons/48-deadcode.png new file mode 100644 index 0000000000..f3224021a5 Binary files /dev/null and b/shells/webextension/icons/48-deadcode.png differ diff --git a/shells/webextension/icons/deadcode.svg b/shells/webextension/icons/deadcode.svg new file mode 100644 index 0000000000..ccd6e66906 --- /dev/null +++ b/shells/webextension/icons/deadcode.svg @@ -0,0 +1 @@ +development780780 \ No newline at end of file diff --git a/shells/webextension/popups/deadcode.html b/shells/webextension/popups/deadcode.html new file mode 100644 index 0000000000..2f617cbe7d --- /dev/null +++ b/shells/webextension/popups/deadcode.html @@ -0,0 +1,24 @@ + + +

+ This page includes an extra development build of React. 🚧 +

+

+ The React build on this page includes both development and production versions because dead code elimination has not been applied correctly. +
+
+ This makes its size larger, and causes React to run slower. +
+
+ Make sure to set up dead code elimination before deployment. +

+
+

+ Open the developer tools, and the React tab will appear to the right. +

diff --git a/shells/webextension/popups/development.html b/shells/webextension/popups/development.html index 2f3f80fce5..3f9985b07a 100644 --- a/shells/webextension/popups/development.html +++ b/shells/webextension/popups/development.html @@ -2,7 +2,7 @@ diff --git a/shells/webextension/popups/disabled.html b/shells/webextension/popups/disabled.html index db048781df..061d0d4b4d 100644 --- a/shells/webextension/popups/disabled.html +++ b/shells/webextension/popups/disabled.html @@ -2,7 +2,7 @@ diff --git a/shells/webextension/popups/outdated.html b/shells/webextension/popups/outdated.html index 66269d9180..3933ef48e2 100644 --- a/shells/webextension/popups/outdated.html +++ b/shells/webextension/popups/outdated.html @@ -2,7 +2,7 @@ diff --git a/shells/webextension/popups/production.html b/shells/webextension/popups/production.html index 81417e83c3..6899ead477 100644 --- a/shells/webextension/popups/production.html +++ b/shells/webextension/popups/production.html @@ -2,7 +2,7 @@ diff --git a/shells/webextension/popups/unminified.html b/shells/webextension/popups/unminified.html index d8b8b7428f..043e3d6804 100644 --- a/shells/webextension/popups/unminified.html +++ b/shells/webextension/popups/unminified.html @@ -2,7 +2,7 @@