From 8fdb2c0964512d580eb311ff97f7e3258b3892c7 Mon Sep 17 00:00:00 2001 From: Dustin Masters Date: Fri, 29 Jun 2018 16:28:45 -0700 Subject: [PATCH 1/2] Fix unhelpful error message if React can't be loaded If React fails to load from a user-provided bundle, the script load exception would be hidden from the user, because the exception is stored to a field and then rethrown later. EnsureReactLoaded was getting called before the real exception was thrown, which threw a new (unhelpful) exception. --- src/React.Core/JavaScriptEngineFactory.cs | 2 +- .../Core/JavaScriptEngineFactoryTest.cs | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/React.Core/JavaScriptEngineFactory.cs b/src/React.Core/JavaScriptEngineFactory.cs index e8905b2d0..3f763e1ae 100644 --- a/src/React.Core/JavaScriptEngineFactory.cs +++ b/src/React.Core/JavaScriptEngineFactory.cs @@ -135,7 +135,7 @@ protected virtual void InitialiseEngine(IJsEngine engine) } LoadUserScripts(engine); - if (!_config.LoadReact) + if (!_config.LoadReact && _scriptLoadException == null) { // We expect to user to have loaded their own version of React in the scripts that // were loaded above, let's ensure that's the case. diff --git a/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs b/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs index 925243071..9a076ba83 100644 --- a/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs +++ b/tests/React.Tests/Core/JavaScriptEngineFactoryTest.cs @@ -164,6 +164,28 @@ public void ShouldThrowIfReactVersionNotLoaded() }); } + [Fact] + public void ShouldThrowScriptErrorIfReactFails() + { + var config = new Mock(); + config.Setup(x => x.ScriptsWithoutTransform).Returns(new List {"foo.js"}); + config.Setup(x => x.LoadReact).Returns(false); + var fileSystem = new Mock(); + fileSystem.Setup(x => x.ReadAsString("foo.js")).Returns("FAIL PLZ"); + + var jsEngine = new Mock(); + jsEngine.Setup(x => x.Evaluate("1 + 1")).Returns(2); + jsEngine.Setup(x => x.Execute("FAIL PLZ")).Throws(new JsRuntimeException("Fail") + { + LineNumber = 42, + ColumnNumber = 911, + }); + var factory = CreateFactory(config, fileSystem, () => jsEngine.Object); + + var ex = Assert.Throws(() => factory.GetEngineForCurrentThread()); + Assert.Equal("Error while loading \"foo.js\": Fail\r\nLine: 42\r\nColumn: 911", ex.Message); + } + [Fact] public void ShouldCatchErrorsWhileLoadingScripts() { From 18d608dc6ec9f040391d29924bb9b7710285fef3 Mon Sep 17 00:00:00 2001 From: Dustin Masters Date: Fri, 29 Jun 2018 16:43:42 -0700 Subject: [PATCH 2/2] Avoid crash if setTimeout/clearTimeout is referenced In some cases, React (or other libraries) will assume that setTimeout and clearTimeout are defined on the global scope, without invoking it. We should still throw an error in case a consumer expects this to just work on the server (it won't), but we can avoid a crash by defining the function on the global scope. For context: https://github.com/reactjs/React.NET/issues/555 --- src/React.Core/Resources/shims.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/React.Core/Resources/shims.js b/src/React.Core/Resources/shims.js index ba2edb908..4c83560cf 100644 --- a/src/React.Core/Resources/shims.js +++ b/src/React.Core/Resources/shims.js @@ -3,12 +3,12 @@ * All rights reserved. * * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant + * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ var global = global || {}; -var React, ReactDOM, ReactDOMServer; +var React, ReactDOM, ReactDOMServer, setTimeout, clearTimeout; // Basic console shim. Caches all calls to console methods. function MockConsole() { @@ -68,6 +68,20 @@ function ReactNET_initReact() { return false; } +setTimeout = setTimeout || global.setTimeout; +if (setTimeout === undefined) { + setTimeout = function() { + throw new Error('setTimeout is not supported in server-rendered Javascript.'); + } +} + +clearTimeout = clearTimeout || global.clearTimeout; +if (clearTimeout === undefined) { + clearTimeout = function() { + throw new Error('clearTimeout is not supported in server-rendered Javascript.'); + } +} + /** * Polyfill for engines that do not support Object.assign */ @@ -94,4 +108,4 @@ if (typeof Object.assign !== 'function') { } return to; }; -} \ No newline at end of file +}