@sentry/wizard
Version:
Sentry wizard helping you to configure your project
561 lines • 30.6 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReactNative = exports.DOCS_MANUAL_STEPS = exports.REACT_NATIVE_PACKAGE = exports.SENTRY_REACT_NATIVE_PACKAGE = exports.COMPATIBLE_SDK_VERSION = exports.COMPATIBLE_REACT_NATIVE_VERSIONS = void 0;
/* eslint-disable max-lines */
var child_process_1 = require("child_process");
var fs = __importStar(require("fs"));
var inquirer_1 = require("inquirer");
var _ = __importStar(require("lodash"));
var path = __importStar(require("path"));
var util_1 = require("util");
var File_1 = require("../../Helper/File");
var Logging_1 = require("../../Helper/Logging");
var Package_1 = require("../../Helper/Package");
var PackageManager_1 = require("../../Helper/PackageManager");
var SentryCli_1 = require("../../Helper/SentryCli");
var MobileProject_1 = require("./MobileProject");
var BottomBar_1 = require("../../Helper/BottomBar");
var url_1 = require("url");
var xcode = require('xcode');
exports.COMPATIBLE_REACT_NATIVE_VERSIONS = '>=0.69.0';
exports.COMPATIBLE_SDK_VERSION = '>= 5.0.0';
exports.SENTRY_REACT_NATIVE_PACKAGE = '@sentry/react-native';
exports.REACT_NATIVE_PACKAGE = 'react-native';
exports.DOCS_MANUAL_STEPS = 'https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/';
var ReactNative = exports.ReactNative = /** @class */ (function (_super) {
__extends(ReactNative, _super);
function ReactNative(_argv) {
var _this = _super.call(this, _argv) || this;
_this._argv = _argv;
_this.url = _argv.url;
_this._sentryCli = new SentryCli_1.SentryCli(_this._argv);
return _this;
}
ReactNative.prototype.emit = function (answers) {
return __awaiter(this, void 0, void 0, function () {
var userAnswers, packageManager, hasCompatibleReactNativeVersion, hasCompatibleSentryReactNativeVersion, sentryCliProperties, promises, host, orgSlug, projectId, projectIssuesUrl;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this._argv.uninstall) {
return [2 /*return*/, this.uninstall(answers)];
}
return [4 /*yield*/, this.shouldEmit(answers)];
case 1:
if (!(_a.sent())) {
return [2 /*return*/, {}];
}
(0, Logging_1.nl)();
userAnswers = { continue: true };
packageManager = (0, PackageManager_1.getPackageManagerChoice)();
hasCompatibleReactNativeVersion = (0, Package_1.checkPackageVersion)(this._readAppPackage(), exports.REACT_NATIVE_PACKAGE, exports.COMPATIBLE_REACT_NATIVE_VERSIONS, true);
if (!(!hasCompatibleReactNativeVersion && !this._argv.quiet)) return [3 /*break*/, 3];
return [4 /*yield*/, (0, inquirer_1.prompt)({
message: 'Your version of React Native is not compatible with Sentry\'s React Native SDK. Do you want to continue?',
name: 'continue',
default: false,
type: 'confirm',
})];
case 2:
userAnswers = _a.sent();
(0, Logging_1.nl)();
_a.label = 3;
case 3:
if (!userAnswers.continue) {
throw new Error("Please upgrade to a version that is compatible with ".concat(exports.COMPATIBLE_REACT_NATIVE_VERSIONS, ". Or use ").concat(exports.DOCS_MANUAL_STEPS));
}
if (!packageManager) return [3 /*break*/, 5];
BottomBar_1.BottomBar.show("Adding ".concat(exports.SENTRY_REACT_NATIVE_PACKAGE, "..."));
return [4 /*yield*/, packageManager.installPackage(exports.SENTRY_REACT_NATIVE_PACKAGE)];
case 4:
_a.sent();
BottomBar_1.BottomBar.hide();
(0, Logging_1.green)("\u2713 Added `".concat(exports.SENTRY_REACT_NATIVE_PACKAGE, "`"));
_a.label = 5;
case 5:
hasCompatibleSentryReactNativeVersion = (0, Package_1.checkPackageVersion)(this._readAppPackage(), exports.SENTRY_REACT_NATIVE_PACKAGE, exports.COMPATIBLE_SDK_VERSION, true);
if (!(!hasCompatibleSentryReactNativeVersion && !this._argv.quiet)) return [3 /*break*/, 7];
return [4 /*yield*/, (0, inquirer_1.prompt)({
message: "Your version of ".concat(exports.SENTRY_REACT_NATIVE_PACKAGE, " is not compatible with this wizard. Do you want to continue?"),
name: 'continue',
default: false,
type: 'confirm',
})];
case 6:
userAnswers = _a.sent();
(0, Logging_1.nl)();
_a.label = 7;
case 7:
if (!userAnswers.continue) {
throw new Error("Please upgrade to a version that is compatible with ".concat(exports.COMPATIBLE_SDK_VERSION, "."));
}
sentryCliProperties = this._sentryCli.convertAnswersToProperties(answers);
promises = this.getPlatforms(answers).map(function (platform) { return __awaiter(_this, void 0, void 0, function () {
var e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 8, , 9]);
if (!(platform === 'ios')) return [3 /*break*/, 3];
return [4 /*yield*/, (0, File_1.patchMatchingFile)('ios/*.xcodeproj/project.pbxproj', this._patchXcodeProj.bind(this))];
case 1:
_a.sent();
(0, Logging_1.green)('✓ Patched build script in Xcode project.');
BottomBar_1.BottomBar.show('Adding Sentry pods...');
return [4 /*yield*/, this._podInstall()];
case 2:
_a.sent();
BottomBar_1.BottomBar.hide();
(0, Logging_1.green)('✓ Pods installed.');
return [3 /*break*/, 5];
case 3: return [4 /*yield*/, (0, File_1.patchMatchingFile)('**/app/build.gradle', this._patchBuildGradle.bind(this))];
case 4:
_a.sent();
(0, Logging_1.green)('✓ Patched build.gradle file.');
_a.label = 5;
case 5: return [4 /*yield*/, this._patchJsSentryInit(platform, answers)];
case 6:
_a.sent();
return [4 /*yield*/, this._addSentryProperties(platform, sentryCliProperties)];
case 7:
_a.sent();
(0, Logging_1.green)("\u2713 Added sentry.properties file to ".concat(platform));
return [3 /*break*/, 9];
case 8:
e_1 = _a.sent();
(0, Logging_1.red)(e_1);
return [3 /*break*/, 9];
case 9: return [2 /*return*/];
}
});
}); });
return [4 /*yield*/, Promise.all(promises)];
case 8:
_a.sent();
host = null;
try {
host = (new url_1.URL(this.url || '')).host;
}
catch (_error) {
// ignore
}
orgSlug = _.get(answers, 'config.organization.slug', null);
projectId = _.get(answers, 'config.project.id', null);
projectIssuesUrl = host && orgSlug && projectId
? "https://".concat(orgSlug, ".").concat(host, "/issues/?project=").concat(projectId)
: null;
(0, Logging_1.l)("\nTo make sure everything is set up correctly, put the following code snippet into your application.\nThe snippet will create a button that, when tapped, sends a test event to Sentry.\n");
if (projectIssuesUrl) {
(0, Logging_1.l)("After that check your project issues:");
(0, Logging_1.l)(projectIssuesUrl);
(0, Logging_1.nl)();
}
(0, Logging_1.l)("<Button title='Try!' onPress={ () => { Sentry.captureException(new Error('First error')) }}/>");
(0, Logging_1.nl)();
if (!!this._argv.quiet) return [3 /*break*/, 10];
return [4 /*yield*/, (0, inquirer_1.prompt)({
message: 'Have you successfully sent a test event?',
name: 'snippet',
default: true,
type: 'confirm',
})];
case 9:
_a.sent();
_a.label = 10;
case 10: return [2 /*return*/, answers];
}
});
});
};
ReactNative.prototype.uninstall = function (_answers) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, (0, File_1.patchMatchingFile)('**/*.xcodeproj/project.pbxproj', this._unpatchXcodeProj.bind(this))];
case 1:
_a.sent();
return [4 /*yield*/, (0, File_1.patchMatchingFile)('**/app/build.gradle', this._unpatchBuildGradle.bind(this))];
case 2:
_a.sent();
return [2 /*return*/, {}];
}
});
});
};
// eslint-disable-next-line @typescript-eslint/require-await
ReactNative.prototype._shouldConfigurePlatform = function (platform) {
return __awaiter(this, void 0, void 0, function () {
var result, regex;
return __generator(this, function (_a) {
result = false;
if (!(0, File_1.exists)("".concat(platform, "/sentry.properties"))) {
result = true;
this.debug("".concat(platform, "/sentry.properties not exists"));
}
if (!(0, File_1.matchesContent)('**/*.xcodeproj/project.pbxproj', /sentry-cli/gi)) {
result = true;
this.debug('**/*.xcodeproj/project.pbxproj not matched');
}
if (!(0, File_1.matchesContent)('**/app/build.gradle', /sentry\.gradle/gi)) {
result = true;
this.debug('**/app/build.gradle not matched');
}
regex = /Sentry/gi;
if ((0, File_1.exists)("index.".concat(platform, ".js")) &&
!(0, File_1.matchesContent)("index.".concat(platform, ".js"), regex)) {
result = true;
this.debug("index.".concat(platform, ".js not matched"));
}
if ((0, File_1.exists)('App.js') && !(0, File_1.matchesContent)('App.js', regex)) {
result = true;
this.debug('index.js or App.js not matched');
}
if (this._argv.uninstall) {
// if we uninstall we need to invert the result so we remove already patched
// but leave untouched platforms as they are
return [2 /*return*/, !result];
}
return [2 /*return*/, result];
});
});
};
ReactNative.prototype._readAppPackage = function () {
var appPackage = {};
try {
appPackage = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
}
catch (_a) {
// We don't need to have this
}
return appPackage;
};
ReactNative.prototype._podInstall = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, (0, util_1.promisify)(child_process_1.exec)('npx --yes pod-install --non-interactive --quiet')];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
ReactNative.prototype._patchJsSentryInit = function (platform, answers) {
return __awaiter(this, void 0, void 0, function () {
var prefixGlob, suffixGlob, platformGlob, universalGlob, jsFileGlob, jsFileToPatch;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
prefixGlob = '{.,./src}';
suffixGlob = '@(j|t|cj|mj)s?(x)';
platformGlob = "index.".concat(platform, ".").concat(suffixGlob);
universalGlob = "App.".concat(suffixGlob);
jsFileGlob = "".concat(prefixGlob, "/+(").concat(platformGlob, "|").concat(universalGlob, ")");
jsFileToPatch = (0, File_1.matchFiles)(jsFileGlob);
if (!(jsFileToPatch.length !== 0)) return [3 /*break*/, 2];
return [4 /*yield*/, (0, File_1.patchMatchingFile)(jsFileGlob, this._patchJs.bind(this), answers, platform)];
case 1:
_a.sent();
(0, Logging_1.green)("\u2713 Patched ".concat(jsFileToPatch.join(', '), " file(s)."));
return [3 /*break*/, 3];
case 2:
(0, Logging_1.red)("\u2717 Could not find ".concat(platformGlob, " nor ").concat(universalGlob, " files."));
(0, Logging_1.red)('✗ Please, visit https://docs.sentry.io/platforms/react-native');
_a.label = 3;
case 3: return [2 /*return*/];
}
});
});
};
ReactNative.prototype._addSentryProperties = function (platform, properties) {
var _this = this;
var rv = Promise.resolve();
// This will create the ios/android folder before trying to write
// sentry.properties in it which would fail otherwise
if (!fs.existsSync(platform)) {
(0, Logging_1.dim)("".concat(platform, " folder did not exist, creating it."));
fs.mkdirSync(platform);
}
var fn = path.join(platform, 'sentry.properties');
if (platform === 'android' && properties['cli/executable']) {
// We don't need to write the sentry-cli path in the properties file
// since our gradle plugins already pick it up on the correct spot
delete properties['cli/executable'];
}
rv = rv.then(function () {
return fs.writeFileSync(fn, _this._sentryCli.dumpProperties(properties));
});
return rv;
};
ReactNative.prototype._patchJs = function (contents, _filename, answers, platform) {
// since the init call could live in other places too, we really only
// want to do this if we managed to patch any of the other files as well.
if (contents.match(/Sentry.config\(/)) {
return Promise.resolve(null);
}
// if we match @sentry\/react-native somewhere, we already patched the file
// and no longer need to
if (contents.match('@sentry/react-native')) {
return Promise.resolve(contents);
}
var dsn = '__DSN__';
this.getPlatforms(answers).forEach(function (selectedPlatform) {
if (platform && selectedPlatform === platform) {
dsn = _.get(answers, 'config.dsn.public', null);
}
else if (platform === undefined) {
dsn = _.get(answers, 'config.dsn.public', null);
}
});
return Promise.resolve(contents.replace(/^([^]*)(import\s+[^;]*?;$)/m, function (match) {
// eslint-disable-next-line prefer-template
return match +
"\n\nimport * as Sentry from '@sentry/react-native';\n\n" +
'Sentry.init({ \n' +
" dsn: '".concat(dsn, "', \n") +
'});\n';
}));
};
// ANDROID -----------------------------------------
ReactNative.prototype._patchBuildGradle = function (contents) {
var applyFrom = 'apply from: "../../node_modules/@sentry/react-native/sentry.gradle"';
if (contents.indexOf(applyFrom) >= 0) {
return Promise.resolve(null);
}
return Promise.resolve(contents.replace(ReactNative._buildGradleAndroidSectionBeginning,
// eslint-disable-next-line prefer-template
function (match) { return applyFrom + '\n' + match; }));
};
ReactNative.prototype._unpatchBuildGradle = function (contents) {
return Promise.resolve(contents.replace(/^\s*apply from: ["']..\/..\/node_modules\/@sentry\/react-native\/sentry.gradle["'];?\s*?\r?\n/m, ''));
};
// IOS -----------------------------------------
ReactNative.prototype._patchExistingXcodeBuildScripts = function (buildScripts) {
for (var _i = 0, buildScripts_1 = buildScripts; _i < buildScripts_1.length; _i++) {
var script = buildScripts_1[_i];
if (!script.shellScript.match(/\/scripts\/react-native-xcode\.sh/i) ||
script.shellScript.match(/sentry-cli\s+react-native\s+xcode/i)) {
continue;
}
var code = JSON.parse(script.shellScript);
code =
// eslint-disable-next-line prefer-template, @typescript-eslint/restrict-plus-operands
'export SENTRY_PROPERTIES=sentry.properties\n' +
'export EXTRA_PACKAGER_ARGS="--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map"\n' +
code.replace('$REACT_NATIVE_XCODE', function () {
// eslint-disable-next-line no-useless-escape
return '\\"../node_modules/@sentry/cli/bin/sentry-cli react-native xcode $REACT_NATIVE_XCODE\\"';
}) +
'\n/bin/sh ../node_modules/@sentry/react-native/scripts/collect-modules.sh\n';
script.shellScript = JSON.stringify(code);
}
};
ReactNative.prototype._addNewXcodeBuildPhaseForSymbols = function (buildScripts, proj) {
for (var _i = 0, buildScripts_2 = buildScripts; _i < buildScripts_2.length; _i++) {
var script = buildScripts_2[_i];
if (script.shellScript.match(/sentry-cli\s+(upload-dsym|debug-files upload)/)) {
return;
}
}
proj.addBuildPhase([], 'PBXShellScriptBuildPhase', 'Upload Debug Symbols to Sentry', null, {
shellPath: '/bin/sh',
shellScript: "\nexport SENTRY_PROPERTIES=sentry.properties\n[[ $SENTRY_INCLUDE_NATIVE_SOURCES == \"true\" ]] && INCLUDE_SOURCES_FLAG=\"--include-sources\" || INCLUDE_SOURCES_FLAG=\"\"\n../node_modules/@sentry/cli/bin/sentry-cli debug-files upload \"$INCLUDE_SOURCES_FLAG\" \"$DWARF_DSYM_FOLDER_PATH\"\n",
});
};
ReactNative.prototype._patchXcodeProj = function (contents, filename) {
var _this = this;
var proj = xcode.project(filename);
return new Promise(function (resolve, reject) {
proj.parse(function (err) {
if (err) {
reject(err);
return;
}
var buildScripts = [];
for (var key in proj.hash.project.objects.PBXShellScriptBuildPhase ||
{}) {
if (
// eslint-disable-next-line no-prototype-builtins
proj.hash.project.objects.PBXShellScriptBuildPhase.hasOwnProperty(key)) {
var val = proj.hash.project.objects.PBXShellScriptBuildPhase[key];
if (val.isa) {
buildScripts.push(val);
}
}
}
try {
_this._patchExistingXcodeBuildScripts(buildScripts);
}
catch (e) {
(0, Logging_1.red)(e);
}
try {
_this._addNewXcodeBuildPhaseForSymbols(buildScripts, proj);
}
catch (e) {
(0, Logging_1.red)(e);
}
// we always modify the xcode file in memory but we only want to save it
// in case the user wants configuration for ios. This is why we check
// here first if changes are made before we might prompt the platform
// continue prompt.
var newContents = proj.writeSync();
if (newContents === contents) {
resolve(undefined);
}
else {
resolve(newContents);
}
});
});
};
ReactNative.prototype._unpatchXcodeBuildScripts = function (proj) {
var scripts = proj.hash.project.objects.PBXShellScriptBuildPhase || {};
var firstTarget = proj.getFirstTarget().uuid;
var nativeTargets = proj.hash.project.objects.PBXNativeTarget;
// scripts to patch partially. Run this first so that we don't
// accidentally delete some scripts later entirely that we only want to
// rewrite.
for (var _i = 0, _a = Object.keys(scripts); _i < _a.length; _i++) {
var key = _a[_i];
var script = scripts[key];
// ignore comments
if (typeof script === 'string') {
continue;
}
// ignore scripts that do not invoke the react-native-xcode command.
if (!script.shellScript.match(/sentry-cli\s+react-native\s+xcode/i)) {
continue;
}
script.shellScript = JSON.stringify(JSON.parse(script.shellScript)
// remove sentry properties export
.replace(/^export SENTRY_PROPERTIES=sentry.properties\r?\n/m, '')
.replace(/^\/bin\/sh ..\/node_modules\/@sentry\/react-native\/scripts\/collect-modules.sh\r?\n/m, '')
// unwrap react-native-xcode.sh command. In case someone replaced it
// entirely with the sentry-cli command we need to put the original
// version back in.
.replace(/\.\.\/node_modules\/@sentry\/cli\/bin\/sentry-cli\s+react-native\s+xcode\s+\$REACT_NATIVE_XCODE/i, '$REACT_NATIVE_XCODE'));
}
// scripts to kill entirely.
for (var _b = 0, _c = Object.keys(scripts); _b < _c.length; _b++) {
var key = _c[_b];
var script = scripts[key];
// ignore comments and keys that got deleted
if (typeof script === 'string' || script === undefined) {
continue;
}
if (script.shellScript.match(/@sentry\/cli\/bin\/sentry-cli\s+(upload-dsym|debug-files upload)\b/)) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete scripts[key];
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete scripts["".concat(key, "_comment")];
var phases = nativeTargets[firstTarget].buildPhases;
if (phases) {
for (var i = 0; i < phases.length; i++) {
if (phases[i].value === key) {
phases.splice(i, 1);
break;
}
}
}
continue;
}
}
};
ReactNative.prototype._unpatchXcodeProj = function (_contents, filename) {
var _this = this;
var proj = xcode.project(filename);
return new Promise(function (resolve, reject) {
proj.parse(function (err) {
if (err) {
reject(err);
return;
}
_this._unpatchXcodeBuildScripts(proj);
resolve(proj.writeSync());
});
});
};
/**
* All React Native versions have app/build.gradle with android section.
*/
ReactNative._buildGradleAndroidSectionBeginning = /^android {/m;
return ReactNative;
}(MobileProject_1.MobileProject));
//# sourceMappingURL=ReactNative.js.map