@wavemaker-ai/wm-reactnative-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +236 -0
  2. package/assets/CLI-EnvironmentVariable.png +0 -0
  3. package/assets/EnvironmentVariable.png +0 -0
  4. package/assets/EnvironmentVariable1.png +0 -0
  5. package/files/ui-build.js +331 -0
  6. package/index.js +381 -0
  7. package/package.json +39 -0
  8. package/src/android.js +479 -0
  9. package/src/command.js +552 -0
  10. package/src/config.js +11 -0
  11. package/src/custom-logger/progress-bar.js +97 -0
  12. package/src/custom-logger/steps.js +117 -0
  13. package/src/custom-logger/task-logger.js +147 -0
  14. package/src/exec.js +73 -0
  15. package/src/expo-launcher.js +596 -0
  16. package/src/ios.js +517 -0
  17. package/src/logger.js +104 -0
  18. package/src/mobileprovision-parse/index.js +72 -0
  19. package/src/project-sync.service.js +390 -0
  20. package/src/requirements.js +250 -0
  21. package/src/utils.js +100 -0
  22. package/src/web-preview-launcher.js +548 -0
  23. package/src/zip.js +19 -0
  24. package/templates/embed/android/ReactNativeAppFragment.java +78 -0
  25. package/templates/embed/android/SplashScreenReactActivityLifecycleListener.kt +41 -0
  26. package/templates/embed/android/fragment_react_native_app.xml +14 -0
  27. package/templates/embed/ios/ReactNativeView.h +12 -0
  28. package/templates/embed/ios/ReactNativeView.m +59 -0
  29. package/templates/embed/ios/ReactNativeView.swift +53 -0
  30. package/templates/expo-camera-patch/useWebQRScanner.js +100 -0
  31. package/templates/ios-build-patch/podFIlePostInstall.js +72 -0
  32. package/templates/package/packageLock.json +14334 -0
  33. package/templates/wm-rn-runtime/App.js +479 -0
  34. package/templates/wm-rn-runtime/App.navigator.js +109 -0
  35. package/test.js +0 -0
  36. package/tools-site/index.html.template +17 -0
  37. package/tools-site/page_background.svg +99 -0
  38. package/tools-site/qrcode.js +614 -0
  39. package/tools-site/styles.css +39 -0
@@ -0,0 +1,390 @@
1
+ const { URL } = require('url');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const logger = require('./logger');
5
+ const prompt = require('prompt');
6
+ const axios = require('axios');
7
+ const os = require('os');
8
+ const qs = require('qs');
9
+ const semver = require('semver');
10
+ const { exec } = require('./exec');
11
+ const { unzip } = require('./zip');
12
+ const taskLogger = require('./custom-logger/task-logger').spinnerBar;
13
+ const {previewSteps} = require('./custom-logger/steps');
14
+ const chalk = require('chalk');
15
+ const { updateIsAiPlatform } = require('./utils');
16
+ //const PULL_URL = '/studio/services/projects/${projectId}/vcs/remoteChanges';
17
+ const STORE_KEY = 'user.auth.token';
18
+ const MAX_REQUEST_ALLOWED_TIME = 5 * 60 * 1000;
19
+ const loggerLabel = 'project-sync-service';
20
+ let remoteBaseCommitId = '';
21
+ let WM_PLATFORM_VERSION = '';
22
+
23
+ async function findProjectId(config) {
24
+ const projectList = (await axios.get(`${config.baseUrl}/edn-services/rest/users/projects/list`,
25
+ {headers: {
26
+ cookie: config.authCookie
27
+ }})).data;
28
+ const project = projectList.filter(p => p.displayName === config.projectName)
29
+ .filter(p => (config.appPreviewUrl.endsWith(p.name + "_" + p.vcsBranchId)));
30
+ if (project && project.length) {
31
+ WM_PLATFORM_VERSION = project[0].platformVersion;
32
+ const is_ai_platform = /\.wavemaker\.ai/.test(config.baseUrl);
33
+ updateIsAiPlatform(is_ai_platform);
34
+ return project[0].studioProjectId;
35
+ }
36
+ }
37
+
38
+ async function downloadFile(res, tempFile){
39
+ if (res.status !== 200) {
40
+ throw new Error('failed to download the project');
41
+ }
42
+ await new Promise((resolve, reject) => {
43
+ const fw = fs.createWriteStream(tempFile);
44
+ res.data.pipe(fw);
45
+ fw.on('error', err => {
46
+ reject(err);
47
+ fw.close();
48
+ });
49
+ fw.on('close', resolve);
50
+ });
51
+ }
52
+
53
+ async function downloadProject(projectId, config, projectDir) {
54
+ try {
55
+ const start = Date.now();
56
+ logger.info({label: loggerLabel,message: 'downloading the project...'});
57
+ taskLogger.start(previewSteps[2].start);
58
+ taskLogger.setTotal(previewSteps[2].total)
59
+ const tempFile = `${os.tmpdir()}/changes_${Date.now()}.zip`;
60
+ if (semver.lt(WM_PLATFORM_VERSION, '11.4.0') && !global.IS_AI_PLATFORM) {
61
+ const res = await axios.get(`${config.baseUrl}/studio/services/projects/${projectId}/vcs/gitInit`, {
62
+ responseType: 'stream',
63
+ headers: {
64
+ cookie: config.authCookie
65
+ }
66
+ });
67
+ taskLogger.incrementProgress(2);
68
+ await downloadFile(res, tempFile);
69
+ taskLogger.incrementProgress(1);
70
+ const gitDir = path.join(projectDir, '.git');
71
+ fs.mkdirpSync(gitDir);
72
+ await unzip(tempFile, gitDir);
73
+ await exec('git', ['restore', '.'], {cwd: projectDir});
74
+ taskLogger.incrementProgress(1);
75
+ }
76
+ else{
77
+ const gitInfo = await axios.get(`${config.baseUrl}/studio/services/projects/${projectId}/vcs/gitBare`, {
78
+ responseType: 'application/json',
79
+ headers: {
80
+ cookie: config.authCookie
81
+ }
82
+ });
83
+ taskLogger.incrementProgress(2);
84
+ if(gitInfo.status !== 200){
85
+ throw new Error('failed to download the project');
86
+ }
87
+ const fileId = gitInfo.data.fileId;
88
+ remoteBaseCommitId = gitInfo.data.remoteBaseCommitId;
89
+ const res = await axios.get(`${config.baseUrl}/file-service/${fileId}`, {
90
+ responseType: 'stream',
91
+ headers: {
92
+ cookie: config.authCookie
93
+ }
94
+ })
95
+ taskLogger.incrementProgress(2);
96
+ await downloadFile(res, tempFile);
97
+ const tempDir = path.join(`${os.tmpdir()}`, `project_${Date.now()}`);
98
+ fs.mkdirpSync(tempDir);
99
+ const gitDir = path.join(projectDir, '.git');
100
+ if(fs.existsSync(gitDir)){
101
+ await unzip(tempFile, gitDir);
102
+ await exec('git', ['config', '--local', '--unset', 'core.bare'], {cwd: projectDir});
103
+ await exec('git', ['restore', '.'], {cwd: projectDir});
104
+ }
105
+ else{
106
+ await unzip(tempFile, tempDir);
107
+ fs.rmSync(projectDir, { recursive: true, force: true });
108
+ await exec('git', ['clone', "-b", "master", tempDir, projectDir]);
109
+ }
110
+ fs.rmSync(tempDir, { recursive: true, force: true });
111
+ taskLogger.incrementProgress(1);
112
+ }
113
+ logger.info({
114
+ label: loggerLabel,
115
+ message: `downloaded the project in (${Date.now() - start} ms).`
116
+ });
117
+ taskLogger.incrementProgress(1);
118
+ taskLogger.succeed(`${previewSteps[2].succeed} in (${Date.now() - start} ms).`);
119
+ fs.unlink(tempFile);
120
+
121
+ const logDirectory = projectDir + '/output/logs/';
122
+ fs.mkdirSync(logDirectory, {
123
+ recursive: true
124
+ });
125
+ logger.info({
126
+ label: loggerLabel,
127
+ message: 'log directory = '+ logDirectory
128
+ });
129
+ global.logDirectory = logDirectory;
130
+ logger.setLogDirectory(logDirectory);
131
+ taskLogger.info("Full log details can be found in: " + chalk.blue(logDirectory));
132
+ } catch (e) {
133
+ logger.info({
134
+ label: loggerLabel,
135
+ message: e+` The download of the project has encountered an issue. Please ensure that the preview is active.`
136
+ });
137
+ taskLogger.fail(e+` ${previewSteps[2].fail}`)
138
+ }
139
+ }
140
+
141
+ async function gitResetAndPull(tempDir, projectDir){
142
+ await exec('git', ['clean', '-fd', '-e', 'output'], {cwd: projectDir});
143
+ await exec('git', ['fetch', path.join(tempDir, 'remoteChanges.bundle'), 'refs/heads/master'], {cwd: projectDir});
144
+ await exec('git', ['reset', '--hard', 'FETCH_HEAD'], {cwd: projectDir});
145
+ }
146
+
147
+ async function pullChanges(projectId, config, projectDir) {
148
+ try {
149
+ const output = await exec('git', ['rev-parse', 'HEAD'], {
150
+ cwd: projectDir
151
+ });
152
+ const headCommitId = output[0];
153
+ logger.debug({label: loggerLabel, message: 'HEAD commit id is ' + headCommitId});
154
+ taskLogger.start('pulling new changes from studio...');
155
+ const tempDir = path.join(`${os.tmpdir()}`, `changes_${Date.now()}`);
156
+ if (semver.lt(WM_PLATFORM_VERSION, '11.4.0') && !global.IS_AI_PLATFORM) {
157
+ const tempFile = `${os.tmpdir()}/changes_${Date.now()}.zip`;
158
+ console.log(tempFile);
159
+ const res = await axios.get(`${config.baseUrl}/studio/services/projects/${projectId}/vcs/remoteChanges?headCommitId=${headCommitId}`, {
160
+ responseType: 'stream',
161
+ headers: {
162
+ cookie: config.authCookie
163
+ }
164
+ });
165
+ await downloadFile(res, tempFile);
166
+ fs.mkdirpSync(tempDir);
167
+ await unzip(tempFile, tempDir);
168
+
169
+ await gitResetAndPull(tempDir, projectDir);
170
+ await exec('git', ['apply', '--allow-empty', '--ignore-space-change', path.join(tempDir, 'patchFile.patch')], {cwd: projectDir});
171
+ logger.debug({label: loggerLabel, message: 'Copying any uncommitted binary files'});
172
+ copyContentsRecursiveSync(path.join(tempDir, 'binaryFiles'), projectDir);
173
+ fs.unlink(tempFile);
174
+ }
175
+ else{
176
+ const gitInfo = await axios.get(`${config.baseUrl}/studio/services/projects/${projectId}/vcs/pull?lastPulledWorkspaceCommitId=${headCommitId}&lastPulledRemoteHeadCommitId=${remoteBaseCommitId}`, {
177
+ responseType: 'application/json',
178
+ headers: {
179
+ cookie: config.authCookie
180
+ }
181
+ });
182
+ if (gitInfo.status !== 200) {
183
+ throw new Error('failed to pull project changes');
184
+ }
185
+ const fileId = gitInfo.data.fileId;
186
+ remoteBaseCommitId = gitInfo.data.remoteBaseCommitId;
187
+ const res = await axios.get(`${config.baseUrl}/file-service/${fileId}`, {
188
+ responseType: 'stream',
189
+ headers: {
190
+ cookie: config.authCookie
191
+ }
192
+ })
193
+ fs.mkdirpSync(tempDir);
194
+ const tempFile = `${tempDir}/remoteChanges.bundle`;
195
+ await downloadFile(res, tempFile);
196
+ await gitResetAndPull(tempDir, projectDir);
197
+ fs.unlink(tempFile);
198
+ }
199
+ fs.rmSync(tempDir, { recursive: true, force: true });
200
+ taskLogger.succeed(`pulled new changes from studio - head commit id ${headCommitId}`);
201
+ let filesChanged = await exec('git', ['diff','--name-status', 'HEAD~1', 'HEAD'], {cwd: projectDir});
202
+ filesChanged = filesChanged.filter(Boolean);
203
+ const changes = filesChanged.map((line) => {
204
+ const [status, ...fileParts] = line.trim().split(/\s+/);
205
+ const filePath = fileParts.join(' ').replace(/^.*webapp\//, '');
206
+ return { status, filePath };
207
+ });
208
+
209
+ const formatted = changes.map(({ status, filePath }) => {
210
+ const color = status === 'A' ? chalk.green
211
+ : status === 'D' ? chalk.red
212
+ : status === 'M' ? chalk.yellow
213
+ : chalk.cyan;
214
+
215
+ return `${color(status)}:${color(filePath)}`;
216
+ });
217
+ taskLogger.info("Files changed: \n\t" + formatted.join('\n\t'));
218
+ } catch (e) {
219
+ logger.info({
220
+ label: loggerLabel,
221
+ message: e+` The attempt to execute "git pull" was unsuccessful. Please verify your connections.`
222
+ });
223
+ taskLogger.succeed( e+` The attempt to execute "git pull" was unsuccessful. Please verify your connections.`);
224
+ }
225
+ }
226
+
227
+ function copyContentsRecursiveSync(src, dest) {
228
+ fs.readdirSync(src).forEach(function(file) {
229
+ var childSrc = path.join(src, file);
230
+ var childDest = path.join(dest, file);
231
+ var exists = fs.existsSync(childSrc);
232
+ var stats = exists && fs.statSync(childSrc);
233
+ var isDirectory = exists && stats.isDirectory();
234
+ if (isDirectory) {
235
+ if (!fs.existsSync(childDest)) {
236
+ fs.mkdirSync(childDest);
237
+ }
238
+ copyContentsRecursiveSync(childSrc, childDest);
239
+ } else {
240
+ fs.copyFileSync(childSrc, childDest);
241
+ }
242
+ });
243
+ }
244
+
245
+ function extractAuthCookie(res) {
246
+ const headers = res && res.response && res.response.headers;
247
+ if (!headers) {
248
+ return;
249
+ }
250
+ const result = headers['set-cookie'].filter(s => s.indexOf('auth_cookie') >= 0);
251
+ if (result.length) {
252
+ return result[0].split(';')[0];
253
+ }
254
+ }
255
+
256
+ async function authenticateWithUserNameAndPassword(config) {
257
+ const credentials = await getUserCredentials();
258
+ return axios.post(`${config.baseUrl}/login/authenticate`,
259
+ qs.stringify({
260
+ j_username: credentials.username,
261
+ j_password: credentials.password
262
+ }), {
263
+ maxRedirects: 0
264
+ }).catch((res) => {
265
+ const cookie = extractAuthCookie(res);
266
+ if (!cookie) {
267
+ console.log('Not able to login. Try again.');
268
+ return authenticate(config);
269
+ }
270
+ return cookie;
271
+ });
272
+ }
273
+
274
+ async function authenticateWithToken(config, showHelp) {
275
+ try {
276
+ if (showHelp) {
277
+ console.log('***************************************************************************************');
278
+ console.log('* Please open the below url in the browser, where your WaveMaker studio is opened. *');
279
+ console.log('* Copy the response content and paste in the terminal. *');
280
+ console.log('***************************************************************************************');
281
+ console.log(`\n\n`);
282
+ console.log(`${config.baseUrl}/studio/services/auth/token`);
283
+ console.log(`\n\n`);
284
+ }
285
+ const cookie = (await getAuthToken()).token.split(';')[0];
286
+ if (!cookie) {
287
+ console.log('Not able to login. Try again.');
288
+ return authenticateWithToken(config);
289
+ }
290
+ return 'auth_cookie='+cookie;
291
+ } catch (e) {
292
+ logger.info({
293
+ label: loggerLabel,
294
+ message: e+` Your authentication has failed. Please proceed with a valid token.`
295
+ });
296
+ }
297
+ }
298
+
299
+ function getUserCredentials() {
300
+ var schema = {
301
+ properties: {
302
+ username: {
303
+ required: true
304
+ },
305
+ password: {
306
+ required: true,
307
+ hidden: true
308
+ }
309
+ }
310
+ };
311
+ prompt.start();
312
+ return new Promise((resolve, reject) => {
313
+ prompt.get(schema, (err, result) => {
314
+ if (err) {
315
+ reject(err);
316
+ } else {
317
+ resolve(result);
318
+ }
319
+ });
320
+ });
321
+ }
322
+
323
+ function getAuthToken() {
324
+ var schema = {
325
+ properties: {
326
+ token: {
327
+ required: true
328
+ }
329
+ }
330
+ };
331
+ prompt.start();
332
+ return new Promise((resolve, reject) => {
333
+ prompt.get(schema, (err, result) => {
334
+ if (err) {
335
+ reject(err);
336
+ } else {
337
+ resolve(result);
338
+ }
339
+ });
340
+ });
341
+ }
342
+
343
+ async function checkAuthCookie(config) {
344
+ try {
345
+ await findProjectId(config);
346
+ logger.info({
347
+ label: loggerLabel,
348
+ message: `user authenticated.`
349
+ });
350
+ } catch(e) {
351
+ return false;
352
+ }
353
+ return true;
354
+ }
355
+
356
+ async function setup(previewUrl, projectName, authToken) {
357
+ if (authToken) {
358
+ authToken = 'auth_cookie=' + authToken;
359
+ }
360
+ if (previewUrl.endsWith('/')) {
361
+ previewUrl = previewUrl.slice(0, -1);
362
+ }
363
+ const config = {
364
+ authCookie : authToken || global.localStorage.getItem(STORE_KEY) || '',
365
+ baseUrl: new URL(previewUrl).origin,
366
+ appPreviewUrl: previewUrl,
367
+ projectName: projectName
368
+ };
369
+ const isAuthenticated = await checkAuthCookie(config);
370
+ if (!isAuthenticated) {
371
+ //console.log(`Need to login to Studio (${config.baseUrl}). \n Please enter your Studio credentails.`);
372
+ //config.authCookie = await authenticateWithUserNameAndPassword(config);
373
+ config.authCookie = await authenticateWithToken(config, true);
374
+ }
375
+ global.localStorage.setItem(STORE_KEY, config.authCookie);
376
+ taskLogger.incrementProgress(1);
377
+ taskLogger.succeed(previewSteps[1].succeed);
378
+ return config;
379
+ }
380
+
381
+ async function setupProject(previewUrl, projectName, toDir, authToken) {
382
+ const config = await setup(previewUrl, projectName, authToken);
383
+ const projectId = await findProjectId(config);
384
+ await downloadProject(projectId, config, toDir);
385
+ return () => pullChanges(projectId, config, toDir);
386
+ };
387
+
388
+ module.exports = {
389
+ setupProject : setupProject
390
+ };
@@ -0,0 +1,250 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const semver = require('semver');
4
+ const prompt = require('prompt');
5
+
6
+ const logger = require('./logger');
7
+ const {
8
+ exec
9
+ } = require('./exec');
10
+ const loggerLabel = 'rn-cli-requirements';
11
+ let VERSIONS = {
12
+ 'NODE': '14.0.0',
13
+ 'POD' : '1.9.0',
14
+ 'JAVA': '11.0.0',
15
+ 'REACT_NATIVE': '0.68.2',
16
+ 'EXPO': '5.4.4',
17
+ }
18
+
19
+ // check if expo cli is installed globally or not
20
+ // gradle check
21
+ async function checkAvailability(cmd, transformFn, projectSrc) {
22
+ try {
23
+ let options = {};
24
+ if (projectSrc) {
25
+ options = {
26
+ cwd: projectSrc
27
+ }
28
+ }
29
+ let output = (await exec(cmd, ['--version'])).join('');
30
+
31
+ if (transformFn) {
32
+ output = transformFn(output);
33
+ }
34
+ // to just return version in x.x.x format
35
+ let version = output.match(/[0-9]+\.[0-9\.]+/)[0];
36
+
37
+ logger.info({
38
+ 'label': loggerLabel,
39
+ 'message': cmd + ' version available is ' + version
40
+ })
41
+ const requiredVersion = VERSIONS[cmd.toUpperCase()];
42
+ version = semver.coerce(version).version;
43
+ if (requiredVersion && semver.lt(version, requiredVersion)) {
44
+ logger.error('Minimum ' + cmd + ' version required is ' + requiredVersion + '. Please update the version.');
45
+ return false;
46
+ }
47
+ return version;
48
+ } catch(e) {
49
+ console.error(e);
50
+ logger.error('Observing error while checking ' + cmd.toUpperCase() + ' availability');
51
+ return false;
52
+ }
53
+ }
54
+
55
+ async function checkForGradleAvailability() {
56
+ return await checkAvailability('gradle', o => o && o.substring(o.indexOf('Gradle')) );
57
+ }
58
+
59
+ async function checkForAndroidStudioAvailability() {
60
+ // ANDROID_HOME environment variable is set or not. If it is set checking if its a valid path or no.
61
+ const ANDROID_HOME = process.env['ANDROID_HOME'];
62
+ const ANDROID_SDK_ROOT = process.env['ANDROID_SDK_ROOT']
63
+ if (ANDROID_HOME && !ANDROID_SDK_ROOT) {
64
+ logger.warn({
65
+ 'label': loggerLabel,
66
+ 'message': 'ANDROID_HOME is deprecated. Recommended to set ANDROID_SDK_ROOT'
67
+ });
68
+ }
69
+ envVariable = ANDROID_SDK_ROOT || ANDROID_HOME;
70
+ if (!envVariable) {
71
+ logger.error({
72
+ 'label': loggerLabel,
73
+ 'message': 'Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
74
+ 'Try update your \'PATH\' to include path to valid SDK directory.'});
75
+ return false;
76
+ }
77
+ if (!fs.existsSync(envVariable)) {
78
+ logger.error({
79
+ 'label': loggerLabel,
80
+ 'message': '\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
81
+ '\nTry update it manually to point to valid SDK directory.'});
82
+ return false;
83
+ }
84
+ let sdkPath = envVariable + '/tools/bin/sdkmanager';
85
+
86
+ // file extension has to be added for windows os for existsSync to work.
87
+ sdkPath = os.type().includes('Windows') ? sdkPath + '.bat' : sdkPath;
88
+
89
+ if (fs.existsSync(sdkPath)) {
90
+ logger.info({
91
+ 'label': loggerLabel,
92
+ 'message': 'Found Android SDK manager at ' + sdkPath
93
+ });
94
+ try {
95
+ await exec(sdkPath, ['--list']);
96
+ } catch(e) {
97
+ console.warn(e);
98
+ }
99
+ } else {
100
+ logger.warn({
101
+ 'label': loggerLabel,
102
+ 'message': 'Failed to find \'android-sdk\' in your \'PATH\'. Install Android-Studio before proceeding to build.'});
103
+ }
104
+ return true;
105
+ }
106
+
107
+ async function hasValidJavaVersion() {
108
+ const javaVersion = (await exec('java', ['-version'])).join('').match(/[0-9\.]+/)[0];
109
+
110
+ if (semver.lt(semver.coerce(javaVersion).version, VERSIONS.JAVA)) {
111
+ logger.error('Minimum java version required is' + VERSIONS.JAVA + '. Please update the java version.');
112
+ return false;
113
+ }
114
+
115
+ const envVariable = process.env['JAVA_HOME'];
116
+
117
+ if (!envVariable) {
118
+ logger.error({
119
+ 'label': loggerLabel,
120
+ 'message': 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.\n' +
121
+ 'Try update your \'PATH\' to include path to valid directory.'});
122
+ return false;
123
+ }
124
+ return true;
125
+ }
126
+
127
+ async function isGitInstalled() {
128
+ return await checkAvailability('git');
129
+ }
130
+
131
+ async function hasYarnPackage() {
132
+ return await checkAvailability('yarn');
133
+ }
134
+
135
+ async function isCocoaPodsInstalled() {
136
+ return await checkAvailability('pod');
137
+ }
138
+
139
+ async function hasValidNodeVersion() {
140
+ return await checkAvailability('node');
141
+ }
142
+
143
+ async function hasValidExpoVersion() {
144
+ // return await checkAvailability('expo');
145
+ return true;
146
+ }
147
+
148
+ function validateForAndroid(keyStore, storePassword, keyAlias, keyPassword) {
149
+ let errors = [];
150
+ if (!(keyStore && fs.existsSync(keyStore))) {
151
+ errors.push(`keystore is required (valid file): ${keyStore}`);
152
+ }
153
+ if (!keyAlias) {
154
+ errors.push('keyAlias is required.');
155
+ }
156
+ if (!keyPassword) {
157
+ errors.push('keyPassword is required.');
158
+ }
159
+ if (!storePassword) {
160
+ errors.push('storePassword is required.');
161
+ }
162
+ return errors;
163
+ }
164
+
165
+ function validateForIos(certificate, password, provisionalFilePath, buildType) {
166
+ let errors = [];
167
+ if (!(certificate && fs.existsSync(certificate))) {
168
+ errors.push(`p12 certificate does not exists : ${certificate}`);
169
+ }
170
+ if (!password) {
171
+ errors.push('password to unlock certificate is required.');
172
+ }
173
+ if (!(provisionalFilePath && fs.existsSync(provisionalFilePath))) {
174
+ errors.push(`Provisional file does not exists : ${provisionalFilePath}`);
175
+ }
176
+ if (!buildType) {
177
+ errors.push('Package type is required.');
178
+ }
179
+ return errors;
180
+ }
181
+
182
+ async function showConfirmation(message) {
183
+ return new Promise((resolve, reject) => {
184
+ prompt.get({
185
+ properties: {
186
+ confirm: {
187
+ pattern: /^(yes|no|y|n)$/gi,
188
+ description: message,
189
+ message: 'Type yes/no',
190
+ required: true,
191
+ default: 'no'
192
+ }
193
+ }
194
+ }, function (err, result) {
195
+ if (err) {
196
+ reject();
197
+ }
198
+ resolve(result.confirm.toLowerCase());
199
+ });
200
+ });
201
+ }
202
+
203
+ async function canDoEmbed() {
204
+ let flag = true;
205
+ flag = flag && await hasValidNodeVersion();
206
+ flag = flag && await hasYarnPackage();
207
+ flag = flag && await isGitInstalled();
208
+ flag = flag && await hasValidExpoVersion();
209
+ return flag;
210
+ }
211
+
212
+ async function canDoIosBuild() {
213
+ let flag = true;
214
+ flag = flag && await hasValidNodeVersion();
215
+ flag = flag && await hasYarnPackage();
216
+ flag = flag && await isGitInstalled();
217
+ flag = flag && await hasValidExpoVersion();
218
+ flag = flag && await isCocoaPodsInstalled();
219
+ return flag;
220
+ }
221
+
222
+ async function canDoAndroidBuild() {
223
+ let flag = true;
224
+ flag = flag && !!await hasValidNodeVersion();
225
+ flag = flag && !!await hasYarnPackage();
226
+ flag = flag && !!await isGitInstalled();
227
+ flag = flag && !!await hasValidExpoVersion();
228
+ flag = flag && !!await hasValidJavaVersion();
229
+ flag = flag && !!await checkForGradleAvailability();
230
+ return flag;
231
+ }
232
+
233
+ module.exports = {
234
+ validateForIos: validateForIos,
235
+ validateForAndroid: validateForAndroid,
236
+ isCocoaPodsInstalled: isCocoaPodsInstalled,
237
+ isGitInstalled: isGitInstalled,
238
+ hasYarnPackage: hasYarnPackage,
239
+ hasValidNodeVersion: hasValidNodeVersion,
240
+ hasValidJavaVersion: hasValidJavaVersion,
241
+ showConfirmation: showConfirmation,
242
+ checkForAndroidStudioAvailability: checkForAndroidStudioAvailability,
243
+ checkForGradleAvailability: checkForGradleAvailability,
244
+ hasValidExpoVersion: hasValidExpoVersion,
245
+ VERSIONS: VERSIONS,
246
+ canDoEmbed: canDoEmbed,
247
+ canDoIosBuild: canDoIosBuild,
248
+ canDoAndroidBuild: canDoAndroidBuild
249
+ }
250
+ // TODO: support for multiple react native versions.