diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index 422447d46..fda065d4f 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -57,6 +57,8 @@ function setupDocumentShim() { } } +var sourceMapPromise; + function handleErrorWithRedBox(e) { var RKExceptionsManager = require('NativeModules').RKExceptionsManager; var errorToString = require('errorToString'); @@ -73,13 +75,14 @@ function handleErrorWithRedBox(e) { if (RKExceptionsManager) { RKExceptionsManager.reportUnhandledException(e.message, errorToString(e)); if (__DEV__) { - try { - var sourceMapInstance = loadSourceMap(); - var prettyStack = errorToString(e, sourceMapInstance); - RKExceptionsManager.updateExceptionMessage(e.message, prettyStack); - } catch (ee) { - GLOBAL.console.error('#CLOWNTOWN (error while displaying error): ' + ee.message); - } + (sourceMapPromise = sourceMapPromise || loadSourceMap()) + .then(map => { + var prettyStack = errorToString(e, map); + RKExceptionsManager.updateExceptionMessage(e.message, prettyStack); + }) + .then(null, error => { + GLOBAL.console.error('#CLOWNTOWN (error while displaying error): ' + error.message); + }); } } } diff --git a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js index 076f9fa5c..48fd9d1bf 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js +++ b/Libraries/JavaScriptAppEngine/Initialization/loadSourceMap.js @@ -2,23 +2,42 @@ * Copyright 2004-present Facebook. All Rights Reserved. * * @providesModule loadSourceMap + * @flow */ 'use strict'; +var Promise = require('Promise'); +var RCTSourceCode = require('NativeModules').RCTSourceCode; var SourceMapConsumer = require('SourceMap').SourceMapConsumer; +var SourceMapURL = require('./source-map-url'); -var sourceMapInstance; +var fetch = require('fetch'); -function loadSourceMap() { - if (sourceMapInstance !== undefined) { - return sourceMapInstance; +function loadSourceMap(): Promise { + return fetchSourceMap() + .then(map => new SourceMapConsumer(map)); +} + +function fetchSourceMap(): Promise { + if (global.RAW_SOURCE_MAP) { + return Promise.resolve(global.RAW_SOURCE_MAP); } - if (!global.RAW_SOURCE_MAP) { - return null; + + if (!RCTSourceCode) { + return Promise.reject(new Error('RCTSourceCode module is not available')); } - sourceMapInstance = new SourceMapConsumer(global.RAW_SOURCE_MAP); - return sourceMapInstance; + + return new Promise(RCTSourceCode.getScriptText) + .then(extractSourceMapURL) + .then(fetch) + .then(response => response.text()) +} + +function extractSourceMapURL({url, text}): string { + var mapURL = SourceMapURL.getFrom(text); + var baseURL = url.match(/(.+:\/\/.*?)\//)[1]; + return baseURL + mapURL; } module.exports = loadSourceMap; diff --git a/Libraries/JavaScriptAppEngine/Initialization/source-map-url.js b/Libraries/JavaScriptAppEngine/Initialization/source-map-url.js new file mode 100644 index 000000000..c3a53f234 --- /dev/null +++ b/Libraries/JavaScriptAppEngine/Initialization/source-map-url.js @@ -0,0 +1,73 @@ +/** + * Copyright 2004-present Facebook. All Rights Reserved. + * + * This is a third-party micro-library grabbed from: + * https://github.com/lydell/source-map-url + * + * @nolint + */ + +(function() { + var define = null; // Hack to make it work with our packager + +// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) + +void (function(root, factory) { + if (typeof define === "function" && define.amd) { + define(factory) + } else if (typeof exports === "object") { + module.exports = factory() + } else { + root.sourceMappingURL = factory() + } +}(this, function() { + + var innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/ + + var regex = RegExp( + "(?:" + + "/\\*" + + "(?:\\s*\r?\n(?://)?)?" + + "(?:" + innerRegex.source + ")" + + "\\s*" + + "\\*/" + + "|" + + "//(?:" + innerRegex.source + ")" + + ")" + + "\\s*$" + ) + + return { + + regex: regex, + _innerRegex: innerRegex, + + getFrom: function(code) { + var match = code.match(regex) + return (match ? match[1] || match[2] || "" : null) + }, + + existsIn: function(code) { + return regex.test(code) + }, + + removeFrom: function(code) { + return code.replace(regex, "") + }, + + insertBefore: function(code, string) { + var match = code.match(regex) + if (match) { + return code.slice(0, match.index) + string + code.slice(match.index) + } else { + return code + string + } + } + } + +})); + +/** End of the third-party code */ + +})(); diff --git a/ReactKit/Base/RCTRootView.m b/ReactKit/Base/RCTRootView.m index b21e53e25..5f0698e32 100644 --- a/ReactKit/Base/RCTRootView.m +++ b/ReactKit/Base/RCTRootView.m @@ -8,6 +8,7 @@ #import "RCTKeyCommands.h" #import "RCTLog.h" #import "RCTRedBox.h" +#import "RCTSourceCode.h" #import "RCTTouchHandler.h" #import "RCTUIManager.h" #import "RCTUtils.h" @@ -200,6 +201,10 @@ static Class _globalExecutorClass; } // Success! + RCTSourceCode *sourceCodeModule = _bridge.modules[NSStringFromClass([RCTSourceCode class])]; + sourceCodeModule.scriptURL = _scriptURL; + sourceCodeModule.scriptText = rawText; + [_bridge enqueueApplicationScript:rawText url:_scriptURL onComplete:^(NSError *error) { dispatch_async(dispatch_get_main_queue(), ^{ [self bundleFinishedLoading:error]; diff --git a/ReactKit/Modules/RCTSourceCode.h b/ReactKit/Modules/RCTSourceCode.h new file mode 100644 index 000000000..3c9df24df --- /dev/null +++ b/ReactKit/Modules/RCTSourceCode.h @@ -0,0 +1,12 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +#import "RCTBridgeModule.h" + +@interface RCTSourceCode : NSObject + +@property (nonatomic, copy) NSString *scriptText; +@property (nonatomic, copy) NSURL *scriptURL; + +@end diff --git a/ReactKit/Modules/RCTSourceCode.m b/ReactKit/Modules/RCTSourceCode.m new file mode 100644 index 000000000..b8799378f --- /dev/null +++ b/ReactKit/Modules/RCTSourceCode.m @@ -0,0 +1,21 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "RCTSourceCode.h" + +#import "RCTAssert.h" +#import "RCTUtils.h" + +@implementation RCTSourceCode + +- (void)getScriptText:(RCTResponseSenderBlock)successCallback failureCallback:(RCTResponseSenderBlock)failureCallback +{ + RCT_EXPORT(); + if (self.scriptText && self.scriptURL) { + successCallback(@[@{@"text": self.scriptText, @"url":[self.scriptURL absoluteString]}]); + } else { + failureCallback(@[RCTMakeError(@"Source code is not available", nil, nil)]); + } + +} + +@end diff --git a/ReactKit/ReactKit.xcodeproj/project.pbxproj b/ReactKit/ReactKit.xcodeproj/project.pbxproj index 73ecaffef..eed3a13e7 100644 --- a/ReactKit/ReactKit.xcodeproj/project.pbxproj +++ b/ReactKit/ReactKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; }; 134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; }; 134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3A1A6E7F0800051CC8 /* RCTContextExecutor.m */; }; 134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 134FCB3C1A6E7F0800051CC8 /* RCTWebViewExecutor.m */; }; @@ -42,8 +43,8 @@ 14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; }; 58114A161AAE854800E7D092 /* RCTPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A131AAE854800E7D092 /* RCTPicker.m */; }; 58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A151AAE854800E7D092 /* RCTPickerManager.m */; }; - 58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */; }; 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */; }; + 58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */; }; 830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; }; 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 830BA4541A8E3BDA00D53203 /* RCTCache.m */; }; 832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; }; @@ -72,6 +73,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 000E6CE91AB0E97F000CDF4D /* RCTSourceCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSourceCode.h; sourceTree = ""; }; + 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = ""; }; 13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; }; 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; }; 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; }; @@ -149,10 +152,10 @@ 58114A131AAE854800E7D092 /* RCTPicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPicker.m; sourceTree = ""; }; 58114A141AAE854800E7D092 /* RCTPickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPickerManager.h; sourceTree = ""; }; 58114A151AAE854800E7D092 /* RCTPickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPickerManager.m; sourceTree = ""; }; - 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePickerManager.m; sourceTree = ""; }; - 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePickerManager.h; sourceTree = ""; }; 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAsyncLocalStorage.m; sourceTree = ""; }; 58114A4F1AAE93D500E7D092 /* RCTAsyncLocalStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAsyncLocalStorage.h; sourceTree = ""; }; + 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePickerManager.m; sourceTree = ""; }; + 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePickerManager.h; sourceTree = ""; }; 830213F31A654E0800B993E6 /* RCTBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeModule.h; sourceTree = ""; }; 830A229C1A66C68A008503DA /* RCTRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootView.h; sourceTree = ""; }; 830A229D1A66C68A008503DA /* RCTRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootView.m; sourceTree = ""; }; @@ -227,6 +230,8 @@ 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */, 13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */, 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */, + 000E6CE91AB0E97F000CDF4D /* RCTSourceCode.h */, + 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */, 13723B4E1A82FD3C00F88898 /* RCTStatusBarManager.h */, 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */, 13B07FED1A69327A00A75B9A /* RCTTiming.h */, @@ -440,6 +445,7 @@ buildActionMask = 2147483647; files = ( 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */, + 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */, 13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */, 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */, 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */,