-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Handle errors in postlink scripts (#6267)
This PR aims to provide some basic error handling logic to the postlink scripts, which run after `react-native link`, by identifying potential drawbacks and managing the execution flow via try/catch statements and the use of throw under specific conditions. It was developed with the support of the @underscopeio team! I added a few messages I thought are informative, which provide some basic feedback and direct the user to the specific section in the documentation whenever it's possible. The script was mostly optimistic, so I changed this a little bit to throw a few errors when unexpected things occur. On the previous version, errors would either be silent or be incorrectly informed as the library being already linked, as this could not be asserted from the information available during execution. Also, I replaced a few single quote/double quote discrepancies on the files I worked on (went for double quotes since that was predominant among the scripts). But adding linting/prettier to these scripts would be nice too, this could be done in a future PR if there's interest. I'd also like to address the issue of the deprecation warning for `react-native link` on a separate PR, I have a candidate solution that should require minimum changes on the docs and the process. I will send this shortly.
- Loading branch information
Eduardo Pelitti
authored
Jun 8, 2020
1 parent
ab63850
commit daa48ca
Showing
8 changed files
with
327 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,145 @@ | ||
// @ts-check | ||
var path = require('./path'); | ||
var fs = require("fs"); | ||
var {warnn, logn, infon, debugn} = require("./log"); | ||
var path = require("./path"); | ||
var { warnn, logn, infon, debugn, errorn } = require("./log"); | ||
|
||
class ApplicationLinker { | ||
class AppDelegateLinker { | ||
constructor() { | ||
this.appDelegatePath = path.appDelegate; | ||
this.removeUnneededImportsSuccess = false; | ||
this.removeApplicationLaunchContentSuccess = false; | ||
} | ||
|
||
link() { | ||
if (this.appDelegatePath) { | ||
logn("Linking AppDelegate..."); | ||
var appDelegateContents = fs.readFileSync(this.appDelegatePath, "utf8"); | ||
if (this._doesBootstrapNavigation(appDelegateContents)) { | ||
infon("AppDelegate already linked\n"); | ||
return; | ||
} | ||
if (!this.appDelegatePath) { | ||
errorn( | ||
" AppDelegate not found! Does the file exist in the correct folder?\n Please check the manual installation docs:\n https://wix.github.io/react-native-navigation/docs/installing#native-installation" | ||
); | ||
return; | ||
} | ||
|
||
logn("Linking AppDelegate..."); | ||
|
||
var appDelegateContents = fs.readFileSync(this.appDelegatePath, "utf8"); | ||
|
||
try { | ||
appDelegateContents = this._removeUnneededImports(appDelegateContents); | ||
appDelegateContents = this._importNavigation(appDelegateContents); | ||
appDelegateContents = this._bootstrapNavigation(appDelegateContents); | ||
this.removeUnneededImportsSuccess = true; | ||
} catch (e) { | ||
errorn(" " + e.message); | ||
} | ||
|
||
appDelegateContents = this._importNavigation(appDelegateContents); | ||
|
||
appDelegateContents = this._bootstrapNavigation(appDelegateContents); | ||
|
||
try { | ||
appDelegateContents = this._removeApplicationLaunchContent(appDelegateContents); | ||
fs.writeFileSync(this.appDelegatePath, appDelegateContents); | ||
this.removeApplicationLaunchContentSuccess = true; | ||
} catch (e) { | ||
errorn(" " + e.message); | ||
} | ||
|
||
fs.writeFileSync(this.appDelegatePath, appDelegateContents); | ||
|
||
if (this.removeUnneededImportsSuccess && this.removeApplicationLaunchContentSuccess) { | ||
infon("AppDelegate linked successfully!\n"); | ||
} else { | ||
warnn("AppDelegate was partially linked, please check the details above and proceed with the manual installation documentation to complete the linking process.!\n"); | ||
} | ||
} | ||
|
||
_doesBootstrapNavigation(applicationContent) { | ||
return /ReactNativeNavigation\s+bootstrap/.test(applicationContent); | ||
} | ||
_removeUnneededImports(content) { | ||
debugn(" Removing Unneeded imports"); | ||
|
||
_removeUnneededImports(applicationContent) { | ||
const unneededImports = [/\#import\s+\<React\/RCTBridge.h>\s/, /#import\s+\<React\/RCTRootView.h>\s/]; | ||
debugn(" Removing Unneeded imports"); | ||
unneededImports.forEach(unneededImport => { | ||
if (unneededImport.test(applicationContent)) { | ||
applicationContent = applicationContent.replace(unneededImport, ""); | ||
let elementsRemovedCount = 0; | ||
|
||
unneededImports.forEach((unneededImport) => { | ||
if (unneededImport.test(content)) { | ||
content = content.replace(unneededImport, ""); | ||
elementsRemovedCount++; | ||
} | ||
}); | ||
|
||
return applicationContent; | ||
if (unneededImports.length === elementsRemovedCount) { | ||
debugn(" All imports have been removed"); | ||
} else if (elementsRemovedCount === 0) { | ||
warnn( | ||
" No imports could be removed. Check the manual installation docs to verify that everything is properly setup:\n https://wix.github.io/react-native-navigation/docs/installing#native-installation" | ||
); | ||
} else { | ||
throw new Error( | ||
"Some imports were removed. Check the manual installation docs to verify that everything is properly setup:\n https://wix.github.io/react-native-navigation/docs/installing#native-installation" | ||
); | ||
} | ||
|
||
return content; | ||
} | ||
|
||
_importNavigation(content) { | ||
if (!this._doesImportNavigation(content)) { | ||
debugn(" Importing ReactNativeNavigation.h"); | ||
return content.replace(/#import\s+"AppDelegate.h"/, '#import "AppDelegate.h"\n#import <ReactNativeNavigation/ReactNativeNavigation.h>'); | ||
} | ||
|
||
warnn(" AppDelegate already imports ReactNativeNavigation.h"); | ||
return content; | ||
} | ||
|
||
_importNavigation(applicationContent) { | ||
if (!this._doesImportNavigation(applicationContent)) { | ||
debugn(" "); | ||
return applicationContent | ||
.replace(/#import\s+"AppDelegate.h"/, "#import \"AppDelegate.h\"\n#import <ReactNativeNavigation/ReactNativeNavigation.h>") | ||
_bootstrapNavigation(content) { | ||
if (this._doesBootstrapNavigation(content)) { | ||
warnn(" Navigation Bootstrap already present."); | ||
return content; | ||
} | ||
warnn(" AppDelegate already imports Navigation"); | ||
return applicationContent; | ||
|
||
debugn(" Bootstrapping Navigation"); | ||
return content.replace(/RCTBridge.*];/, "[ReactNativeNavigation bootstrapWithDelegate:self launchOptions:launchOptions];"); | ||
} | ||
|
||
_doesBootstrapNavigation(content) { | ||
return /ReactNativeNavigation\s+bootstrap/.test(content); | ||
} | ||
|
||
_removeApplicationLaunchContent(applicationContent) { | ||
const toRemove = [/RCTRootView\s+\*rootView((.|\r|\s)*?)];\s+/, /rootView.backgroundColor((.|\r)*)];\s+/, | ||
/self.window((.|\r)*)];\s+/, /UIViewController\s\*rootViewController((.|\r)*)];\s+/, /rootViewController\.view\s+=\s+rootView;\s+/, | ||
/self.window.rootViewController\s+=\s+rootViewController;\s+/, /\[self.window\s+makeKeyAndVisible];\s+/ | ||
] | ||
_removeApplicationLaunchContent(content) { | ||
debugn(" Removing Application launch content"); | ||
|
||
toRemove.forEach(element => { | ||
if (element.test(applicationContent)) { | ||
applicationContent = applicationContent.replace(element, ""); | ||
const toRemove = [ | ||
/RCTRootView\s+\*rootView((.|\r|\s)*?)];\s+/, | ||
/rootView.backgroundColor((.|\r)*)];\s+/, | ||
/self.window((.|\r)*)];\s+/, | ||
/UIViewController\s\*rootViewController((.|\r)*)];\s+/, | ||
/rootViewController\.view\s+=\s+rootView;\s+/, | ||
/self.window.rootViewController\s+=\s+rootViewController;\s+/, | ||
/\[self.window\s+makeKeyAndVisible];\s+/, | ||
]; | ||
let elementsRemovedCount = 0; | ||
|
||
toRemove.forEach((element) => { | ||
if (element.test(content)) { | ||
content = content.replace(element, ""); | ||
elementsRemovedCount++; | ||
} | ||
}); | ||
|
||
return applicationContent; | ||
} | ||
if (toRemove.length === elementsRemovedCount) { | ||
debugn(" Application Launch content has been removed"); | ||
} else if (elementsRemovedCount === 0) { | ||
warnn( | ||
" No elements could be removed. Check the manual installation docs to verify that everything is properly setup:\n https://wix.github.io/react-native-navigation/docs/installing#native-installation" | ||
); | ||
} else { | ||
throw new Error( | ||
"Some elements were removed. Check the manual installation docs to verify that everything is properly setup:\n https://wix.github.io/react-native-navigation/docs/installing#native-installation" | ||
); | ||
} | ||
|
||
_bootstrapNavigation(applicationContent) { | ||
return applicationContent.replace(/RCTBridge.*];/, "[ReactNativeNavigation bootstrapWithDelegate:self launchOptions:launchOptions];"); | ||
return content; | ||
} | ||
|
||
_doesImportNavigation(applicationContent) { | ||
return /#import\s+\<ReactNativeNavigation\/ReactNativeNavigation.h>/.test(applicationContent); | ||
_doesImportNavigation(content) { | ||
return /#import\s+\<ReactNativeNavigation\/ReactNativeNavigation.h>/.test(content); | ||
} | ||
} | ||
|
||
module.exports = ApplicationLinker; | ||
module.exports = AppDelegateLinker; |
Oops, something went wrong.