@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,596 @@
1
+ const logger = require('./logger');
2
+ const fs = require('fs-extra');
3
+ const express = require('express');
4
+ const http = require('http');
5
+ const request = require('request');
6
+ const os = require('os');
7
+ const rimraf = require("rimraf");
8
+ const open = require('open');
9
+ const httpProxy = require('http-proxy');
10
+ const {
11
+ exec
12
+ } = require('./exec');
13
+ const { readAndReplaceFileContent, isWindowsOS, isExpoWebPreviewContainer, getDestPathForWindows, updatePackageLockFileWithWMRepoScope } = require('./utils');
14
+ const crypto = require('crypto');
15
+ const {VERSIONS, hasValidExpoVersion} = require('./requirements');
16
+ const axios = require('axios');
17
+ const { setupProject } = require('./project-sync.service');
18
+ const path = require('path');
19
+ const semver = require('semver');
20
+ //const openTerminal = require('open-terminal').default;
21
+ const webPreviewPort = 19005;
22
+ let proxyPort = 19009;
23
+ let barcodePort = 19000;
24
+ let proxyUrl = `http://${getIpAddress()}:${proxyPort}`;
25
+ let localHostUrl = `http://localhost:${proxyPort}`
26
+ const loggerLabel = 'expo-launcher';
27
+ function installGlobalNpmPackage(package) {
28
+ return exec('npm', ['install', '-g', package]);
29
+ }
30
+ const taskLogger = require('./custom-logger/task-logger').spinnerBar;
31
+ const {previewSteps} = require('./custom-logger/steps');
32
+ const chalk = require('chalk');
33
+
34
+ var isWebPreview = false;
35
+ var useProxy = false;
36
+ var expoDirectoryHash = "";
37
+ let windowsPreviewDir = "";
38
+ let rnAppPath = "";
39
+ let etag = "";
40
+ let isExpoPreviewContainer = false;
41
+
42
+ function launchServiceProxy(projectDir, previewUrl) {
43
+ const proxy = httpProxy.createProxyServer({});
44
+ const wmProjectDir = getWmProjectDir(projectDir);
45
+ if (isWebPreview) {
46
+ const app = express();
47
+ app.use('/rn-bundle', express.static(wmProjectDir + '/rn-bundle'));
48
+ app.get("*", (req, res) => {
49
+ res.send(`
50
+ <html>
51
+ <head>
52
+ <script type="text/javascript">
53
+ location.href="/rn-bundle/index.html"
54
+ </script>
55
+ </head>
56
+ </html>`);
57
+ });
58
+ app.listen(webPreviewPort);
59
+ }
60
+ http.createServer(function (req, res) {
61
+ try {
62
+ let tUrl = req.url;
63
+ if (req.url === '/' || req.url.startsWith('/rn-bundle')) {
64
+ tUrl = `http://localhost:${webPreviewPort}${req.url}`;
65
+ req.pipe(request(tUrl)).pipe(res);
66
+ } else {
67
+ proxy.web(req, res, {
68
+ target: previewUrl,
69
+ xfwd: false,
70
+ changeOrigin: true,
71
+ secure: false,
72
+ cookiePathRewrite: {
73
+ "*": ""
74
+ }
75
+ });
76
+ tUrl = `${previewUrl}/${req.url}`;
77
+ }
78
+ } catch(e) {
79
+ res.writeHead(500);
80
+ console.error(e);
81
+ }
82
+ }).listen(proxyPort);
83
+ proxy.on('proxyReq', function(proxyReq, req, res, options) {
84
+ proxyReq.setHeader('sec-fetch-mode', 'no-cors');
85
+ proxyReq.setHeader('origin', previewUrl);
86
+ proxyReq.setHeader('referer', previewUrl);
87
+ });
88
+ proxy.on('proxyRes', function(proxyRes, req, res, options) {
89
+ var cookies = proxyRes.headers['set-cookie'];
90
+ if (cookies) {
91
+ cookies = typeof cookies === 'string' ? [cookies] : cookies;
92
+ cookies = cookies.map(c => c.replace(/;?\sSecure/, ''));
93
+ proxyRes.headers['set-cookie'] = cookies;
94
+ }
95
+ });
96
+ proxy.on('error', function(err, req, res){
97
+ logger.error({
98
+ label: loggerLabel,
99
+ message: err
100
+ });
101
+ })
102
+ logger.info({
103
+ label: loggerLabel,
104
+ message: `Service proxy launched at ${proxyUrl} .`
105
+ });
106
+ }
107
+
108
+ function launchToolServer() {
109
+ const app = express();
110
+ const port = 19002;
111
+ const url = `exp://${getIpAddress()}:${barcodePort}/`;
112
+ app.use(express.static(__dirname + '/../tools-site'));
113
+ app.get("/", (req, res) => {
114
+ const template = fs.readFileSync(__dirname+ '/../tools-site/index.html.template', {
115
+ encoding: "utf-8"
116
+ });
117
+ res.send(template.replace(/\{\{url\}\}/g, url));
118
+ });
119
+ app.listen(port);
120
+ logger.info({
121
+ label: loggerLabel,
122
+ message: `open http://localhost:${port}/ in browser.`
123
+ });
124
+ open(`http://localhost:${port}/`);
125
+ }
126
+
127
+ function getIpAddress() {
128
+ var interfaces = os.networkInterfaces();
129
+ for(var key in interfaces) {
130
+ var addresses = interfaces[key];
131
+ for(var i = 0; i < addresses.length; i++) {
132
+ var address = addresses[i];
133
+ if(!address.internal && address.family === 'IPv4') {
134
+ return address.address;
135
+ };
136
+ };
137
+ };
138
+ return 'localhost';
139
+ }
140
+
141
+ async function updatePackageJsonFile(path) {
142
+ let data = fs.readFileSync(path, 'utf-8');
143
+ const jsonData = JSON.parse(data);
144
+ if(semver.eq(jsonData["dependencies"]["expo"], "54.0.12")){
145
+ //do nothing
146
+ }
147
+ else{
148
+ if (jsonData['dependencies']['expo-file-system'] === '^15.1.1') {
149
+ jsonData['dependencies']['expo-file-system'] = '15.2.2'
150
+ }
151
+ if(isWebPreview){
152
+ jsonData['dependencies']['react-native-svg'] = '13.4.0';
153
+ }
154
+ }
155
+ fs.writeFileSync(path, JSON.stringify(jsonData), 'utf-8');
156
+ logger.info({
157
+ 'label': loggerLabel,
158
+ 'message': 'updated package.json file'
159
+ });
160
+ }
161
+
162
+ async function transpile(projectDir, previewUrl, incremental) {
163
+ try{
164
+ taskLogger.start(previewSteps[3].start);
165
+ taskLogger.setTotal(previewSteps[3].total);
166
+ let codegen = process.env.WAVEMAKER_STUDIO_FRONTEND_CODEBASE;
167
+ let packageLockJsonFile = '';
168
+ const expoProjectDir = getExpoProjectDir(projectDir);
169
+ if (codegen) {
170
+ codegen = `${codegen}/wavemaker-rn-codegen/build/index.js`;
171
+ let templatePackageJsonFile = path.resolve(`${process.env.WAVEMAKER_STUDIO_FRONTEND_CODEBASE}/wavemaker-rn-codegen/src/templates/project/package.json`);
172
+ let templatePackageJsonDir = path.resolve(`${process.env.WAVEMAKER_STUDIO_FRONTEND_CODEBASE}/wavemaker-rn-codegen/src/templates/project/`);
173
+ const packageJson = require(templatePackageJsonFile);
174
+ if(semver.eq(packageJson["dependencies"]["expo"], "52.0.17")){
175
+ packageLockJsonFile = path.resolve(`${__dirname}/../templates/package/packageLock.json`);
176
+ }
177
+ if(semver.eq(packageJson["dependencies"]["expo"], "54.0.12")){
178
+ if(isWebPreview){
179
+ packageLockJsonFile = path.resolve(`${__dirname}/../templates/package/packageLock.json`);
180
+ } else {
181
+ packageLockJsonFile = path.resolve(`${__dirname}/../templates/package/packageLock.json`);
182
+ }
183
+ }
184
+ taskLogger.incrementProgress(2);
185
+ } else {
186
+ const wmProjectDir = getWmProjectDir(projectDir);
187
+ codegen = `${projectDir}/target/codegen/node_modules/${global.WM_REPO_SCOPE}/rn-codegen`;
188
+ if (!fs.existsSync(`${codegen}/index.js`)) {
189
+ const temp = projectDir + '/target/codegen';
190
+ fs.mkdirSync(temp, {recursive: true});
191
+ await exec('npm', ['init', '-y'], {
192
+ cwd: temp
193
+ });
194
+ var pom = fs.readFileSync(`${projectDir}/pom.xml`, { encoding: 'utf-8'});
195
+ var uiVersion = ((pom
196
+ && pom.match(/wavemaker.app.runtime.ui.version>(.*)<\/wavemaker.app.runtime.ui.version>/))
197
+ || [])[1];
198
+ await exec('npm', ['install', '--save-dev', `${global.WM_REPO_SCOPE}/rn-codegen@${uiVersion}`], {
199
+ cwd: temp
200
+ });
201
+ taskLogger.incrementProgress(2);
202
+ let version = semver.coerce(uiVersion).version;
203
+ if(semver.gte(version, '11.10.0')){
204
+ rnAppPath = `${projectDir}/target/codegen/node_modules/${global.WM_REPO_SCOPE}/rn-app`;
205
+ await exec('npm', ['install', '--save-dev', `${global.WM_REPO_SCOPE}/rn-app@${uiVersion}`], {
206
+ cwd: temp
207
+ });
208
+ }
209
+ }
210
+ await readAndReplaceFileContent(`${codegen}/src/profiles/expo-preview.profile.js`, (content) => {
211
+ return content.replace('copyResources: false', 'copyResources: true');
212
+ });
213
+ }
214
+ const profile = isWebPreview ? 'web-preview' : 'expo-preview';
215
+ await exec('node',
216
+ [codegen, 'transpile', '--profile="' + profile + '"', '--autoClean=false',
217
+ `--incrementalBuild=${!!incremental}`,
218
+ ...(rnAppPath ? [`--rnAppPath=${rnAppPath}`] : []),
219
+ getWmProjectDir(projectDir), getExpoProjectDir(projectDir)]);
220
+ taskLogger.incrementProgress(2);
221
+ const configJSONFile = `${expoProjectDir}/wm_rn_config.json`;
222
+ const config = fs.readJSONSync(configJSONFile);
223
+ if(packageLockJsonFile){
224
+ generatedExpoPackageLockJsonFile = path.resolve(`${expoProjectDir}/package-lock.json`);
225
+ await fs.copy(packageLockJsonFile, generatedExpoPackageLockJsonFile, { overwrite: false });
226
+ if(global.WM_REPO_SCOPE == '@wavemaker-ai'){
227
+ updatePackageLockFileWithWMRepoScope(generatedExpoPackageLockJsonFile);
228
+ }
229
+ }
230
+ if (isWebPreview) {
231
+ config.serverPath = `${proxyUrl}/_`;
232
+ } else if (useProxy) {
233
+ config.serverPath = `http://${getIpAddress()}:${proxyPort}/`;
234
+ } else {
235
+ config.serverPath = previewUrl;
236
+ }
237
+ fs.writeFileSync(configJSONFile, JSON.stringify(config, null, 4));
238
+ // TODO: iOS app showing blank screen
239
+ if (!(config.sslPinning && config.sslPinning.enabled)) {
240
+ await readAndReplaceFileContent(`${getExpoProjectDir(projectDir)}/App.js`, content => {
241
+ return content.replace('if (isSslPinningAvailable()) {',
242
+ 'if (false && isSslPinningAvailable()) {');
243
+ });
244
+ }
245
+ logger.info({
246
+ label: loggerLabel,
247
+ message: `generated expo project at ${getExpoProjectDir(projectDir)}`
248
+ });
249
+ taskLogger.incrementProgress(2);
250
+ taskLogger.succeed(`${previewSteps[3].succeed}`)
251
+
252
+ }catch(e){
253
+ taskLogger.fail(previewSteps[3].fail);
254
+ }
255
+ }
256
+
257
+ async function installDependencies(projectDir) {
258
+ await updatePackageJsonFile(getExpoProjectDir(projectDir)+ '/package.json');
259
+ if(!isWebPreview){
260
+ try{
261
+ taskLogger.start(previewSteps[4].start);
262
+ taskLogger.setTotal(previewSteps[4].total);
263
+ taskLogger.incrementProgress(1);
264
+ await exec('npm', ['install'], {
265
+ cwd: getExpoProjectDir(projectDir)
266
+ });
267
+ taskLogger.incrementProgress(3);
268
+ taskLogger.succeed(previewSteps[4].succeed);
269
+ }catch(e){
270
+ taskLogger.fail(previewSteps[4].fail);
271
+ }
272
+ }
273
+ }
274
+
275
+ async function launchExpo(projectDir, web) {
276
+ //openTerminal(`cd ${getExpoProjectDir(projectDir)}; expo start --web`);
277
+ const args = ['expo', 'start', ];
278
+ if (web) {
279
+ args.push('--web');
280
+ } else {
281
+ launchToolServer();
282
+ }
283
+ await exec('npx', args, {
284
+ cwd: getExpoProjectDir(projectDir)
285
+ });
286
+ }
287
+
288
+ function clean(path) {
289
+ if (fs.existsSync(path)) {
290
+ rimraf.sync(path, {recursive: true});
291
+ }
292
+ fs.mkdirSync(path, {recursive: true});
293
+ }
294
+
295
+ async function getProjectName(previewUrl) {
296
+ return JSON.parse(
297
+ (await axios.get(`${previewUrl}/services/application/wmProperties.js`))
298
+ .data.split('=')[1].replace(';', '')).displayName;
299
+ }
300
+
301
+ function getWmProjectDir(projectDir) {
302
+ return `${projectDir}/src/main/webapp`;
303
+ }
304
+
305
+ function getExpoProjectDir(projectDir) {
306
+ if (isWebPreview) {
307
+ return `${projectDir}/target/generated-rn-web-app`;
308
+ }
309
+ if (isWindowsOS()) {
310
+ return windowsPreviewDir;
311
+ }
312
+ return `${projectDir}/target/generated-expo-app`;
313
+ }
314
+
315
+ async function setup(previewUrl, _clean, authToken) {
316
+ taskLogger.setTotal(previewSteps[0].total);
317
+ taskLogger.start(previewSteps[0].start);
318
+ taskLogger.incrementProgress(0.5);
319
+ const projectName = await getProjectName(previewUrl);
320
+ const projectDir = `${global.rootDir}/wm-projects/${projectName.replace(/\s+/g, '_').replace(/\(/g, '_').replace(/\)/g, '_')}`;
321
+ if (isWindowsOS()) {
322
+ try {
323
+ windowsPreviewDir = await getDestPathForWindows('preview', projectDir);
324
+ } catch (e) {
325
+ taskLogger.fail(e.message);
326
+ throw e;
327
+ }
328
+ }
329
+ if (_clean) {
330
+ clean(projectDir);
331
+ if (isWindowsOS() && windowsPreviewDir) {
332
+ clean(windowsPreviewDir);
333
+ }
334
+ } else {
335
+ fs.mkdirpSync(getWmProjectDir(projectDir));
336
+ }
337
+ taskLogger.incrementProgress(0.5);
338
+ taskLogger.succeed(previewSteps[0].succeed);
339
+ taskLogger.resetProgressBar();
340
+ taskLogger.setTotal(previewSteps[1].total)
341
+ const syncProject = await setupProject(previewUrl, projectName, projectDir, authToken);
342
+ await transpile(projectDir, previewUrl, false);
343
+ return {projectDir, syncProject};
344
+ }
345
+
346
+ async function watchProjectChanges(previewUrl, onChange, lastModifiedOn) {
347
+ try {
348
+ if(isExpoPreviewContainer){
349
+ const response = await axios.get(`${previewUrl}/rn-bundle/index.bundle?minify=true&platform=web&dev=true&hot=false&transform.engine=hermes&transform.routerRoot=app&unstable_transformProfile=hermes-stable`, {
350
+ headers: {
351
+ 'if-none-match' : etag || ""
352
+ }
353
+ }).catch((e) => e.response);
354
+ etag = response.headers.etag;
355
+ if (response.status === 200) {
356
+ onChange();
357
+ }
358
+ }else{
359
+ const response = await axios.get(`${previewUrl}/rn-bundle/index.html`, {
360
+ headers: {
361
+ 'if-modified-since' : lastModifiedOn || new Date().toString()
362
+ }
363
+ }).catch((e) => e.response);
364
+ if (response.status === 200 && response.data.indexOf('<title>WaveMaker Preview</title>') > 0) {
365
+ lastModifiedOn = response.headers['last-modified'];
366
+ onChange();
367
+ }
368
+ }
369
+ } catch(e) {
370
+ logger.debug({
371
+ label: loggerLabel,
372
+ message: e
373
+ });
374
+ }
375
+ setTimeout(() => watchProjectChanges(previewUrl, onChange, lastModifiedOn), 5000);
376
+ }
377
+ // expo android, ios are throwing errors with reanimated plugin
378
+ // hence modifying the 2.8.0version and just adding chrome debugging fix to this.
379
+ function updateReanimatedPlugin(projectDir) {
380
+ const packageFile = `${getExpoProjectDir(projectDir)}/package.json`;
381
+ const package = JSON.parse(fs.readFileSync(packageFile, {
382
+ encoding: 'utf-8'
383
+ }));
384
+ if (package['dependencies']['expo'] === '48.0.18' || package['dependencies']['expo'] === '49.0.7') {
385
+ let path = getExpoProjectDir(projectDir);
386
+ path = path + '/node_modules/react-native-reanimated/src/reanimated2/NativeReanimated/NativeReanimated.ts';
387
+ let content = fs.readFileSync(path, 'utf-8');
388
+ content = content.replace(/global.__reanimatedModuleProxy === undefined/gm, `global.__reanimatedModuleProxy === undefined && native`);
389
+ fs.writeFileSync(path, content);
390
+ }
391
+ }
392
+
393
+ function getLastModifiedTime(path) {
394
+ if (fs.existsSync(path)) {
395
+ return fs.lstatSync(path).mtime;
396
+ }
397
+ return 0;
398
+ }
399
+ let lastKnownModifiedTime = {
400
+ 'rn-runtime': 0,
401
+ 'rn-codegen': 0,
402
+ 'ui-variables': 0,
403
+ };
404
+
405
+ function watchForPlatformChanges(callBack) {
406
+ let codegen = process.env.WAVEMAKER_STUDIO_FRONTEND_CODEBASE;
407
+ if (!codegen) {
408
+ return;
409
+ }
410
+ setTimeout(() => {
411
+ let currentModifiedTime = {
412
+ 'rn-runtime': getLastModifiedTime(`${codegen}/wavemaker-rn-runtime/dist/new-build`),
413
+ 'rn-codegen': getLastModifiedTime(`${codegen}/wavemaker-rn-codegen/dist/new-build`),
414
+ 'ui-variables': getLastModifiedTime(`${codegen}/wavemaker-ui-variables/dist/new-build`),
415
+ };
416
+
417
+ if (!lastKnownModifiedTime || !lastKnownModifiedTime['rn-runtime']) {
418
+ lastKnownModifiedTime = currentModifiedTime;
419
+ }
420
+
421
+ const doBuild = lastKnownModifiedTime['rn-runtime'] < currentModifiedTime['rn-runtime']
422
+ || lastKnownModifiedTime['rn-codegen'] < currentModifiedTime['rn-codegen']
423
+ || lastKnownModifiedTime['ui-variables'] < currentModifiedTime['ui-variables'];
424
+
425
+ lastKnownModifiedTime = currentModifiedTime;
426
+
427
+ if (doBuild && callBack) {
428
+ // console.log('\n\n\n')
429
+ logger.info({
430
+ label: loggerLabel,
431
+ message: 'Platform Changed. Building again.'
432
+ });
433
+ callBack().then(() => {
434
+ watchForPlatformChanges(callBack);
435
+ });
436
+ } else {
437
+ watchForPlatformChanges(callBack);
438
+ }
439
+ }, 5000);
440
+ }
441
+
442
+ async function runExpo(previewUrl, clean, authToken) {
443
+ try {
444
+ const {projectDir, syncProject} = await setup(previewUrl, clean, authToken);
445
+
446
+ await installDependencies(projectDir);
447
+ if (!isWebPreview) {
448
+ updateReanimatedPlugin(projectDir);
449
+ }
450
+ const packageFile = `${getExpoProjectDir(projectDir)}/package.json`;
451
+ const package = JSON.parse(fs.readFileSync(packageFile, {
452
+ encoding: 'utf-8'
453
+ }));
454
+ barcodePort = package['dependencies']['expo'] === '48.0.18' ? 19000:8081;
455
+ if (useProxy || isWebPreview) {
456
+ launchServiceProxy(projectDir, previewUrl);
457
+ }
458
+ if (!isWebPreview) {
459
+ launchExpo(projectDir);
460
+ }
461
+ taskLogger.info(`generated esbuild web app at ${projectDir}`);
462
+ taskLogger.succeed(chalk.green("Esbuild finished ") + chalk.blue(`Service proxy launched at ${localHostUrl}`));
463
+ isExpoPreviewContainer = await isExpoWebPreviewContainer(previewUrl);
464
+ watchProjectChanges(previewUrl, () => {
465
+ const startTime = Date.now();
466
+ syncProject()
467
+ .then(() => {
468
+ logger.info({
469
+ label: loggerLabel,
470
+ message: `Sync Time: ${(Date.now() - startTime)/ 1000}s.`
471
+ });
472
+ taskLogger.info(`Sync Time: ${(Date.now() - startTime)/ 1000}s.`);
473
+ })
474
+ .then(() => transpile(projectDir, previewUrl, true))
475
+ .then(() => {
476
+ logger.info({
477
+ label: loggerLabel,
478
+ message: `Total Time: ${(Date.now() - startTime)/ 1000}s.`
479
+ });
480
+ taskLogger.info(`Total Time: ${(Date.now() - startTime)/ 1000}s.`);
481
+ });
482
+ });
483
+ watchForPlatformChanges(() => transpile(projectDir, previewUrl, false));
484
+ } catch(e) {
485
+ logger.error({
486
+ label: loggerLabel,
487
+ message: e
488
+ });
489
+ }
490
+ }
491
+
492
+ async function sync(previewUrl, clean) {
493
+ const {projectDir, syncProject} = await setup(previewUrl, clean);
494
+ proxyPort = 19007;
495
+ proxyUrl = `http://${getIpAddress()}:${proxyPort}`;
496
+ await installDependencies(projectDir);
497
+ if (useProxy) {
498
+ launchServiceProxy(projectDir, previewUrl);
499
+ }
500
+ taskLogger.succeed(chalk.green("Sync finished ") + chalk.blue(`generated expo project at : ${getExpoProjectDir(projectDir)}`));
501
+ isExpoPreviewContainer = await isExpoWebPreviewContainer(previewUrl);
502
+ watchProjectChanges(previewUrl, () => {
503
+ const startTime = Date.now();
504
+ syncProject()
505
+ .then(() => {
506
+ logger.info({
507
+ label: loggerLabel,
508
+ message: `Sync Time: ${(Date.now() - startTime)/ 1000}s.`
509
+ });
510
+ taskLogger.info(`Sync Time: ${(Date.now() - startTime)/ 1000}s.`);
511
+ }).then(() => transpile(projectDir, previewUrl, true))
512
+ .then(() => {
513
+ logger.info({
514
+ label: loggerLabel,
515
+ message: `Total Time: ${(Date.now() - startTime)/ 1000}s.`
516
+ });
517
+ taskLogger.info(`Total Time: ${(Date.now() - startTime)/ 1000}s.`);
518
+ });
519
+ });
520
+ watchForPlatformChanges(() => {
521
+ const startTime = Date.now();
522
+ return transpile(projectDir, previewUrl, false).then(() => {
523
+ const duration = ((Date.now() - startTime) / 1000).toFixed(2);
524
+ logger.info({
525
+ label: loggerLabel,
526
+ message: `Total Time: ${duration}s.`
527
+ });
528
+ taskLogger.info(`Total Time: ${duration}s.`);
529
+ });
530
+ });
531
+ }
532
+
533
+ async function runNative(previewUrl, platform, clean) {
534
+ try {
535
+ const {projectDir, syncProject} = await setup(previewUrl, clean);
536
+
537
+ await installDependencies(projectDir);
538
+ updateReanimatedPlugin(projectDir);
539
+ if (useProxy) {
540
+ launchServiceProxy(projectDir, previewUrl);
541
+ }
542
+ await exec('npx', ['expo','prebuild'], {
543
+ cwd: getExpoProjectDir(projectDir)
544
+ });
545
+ await transpile(projectDir, previewUrl, false);
546
+ await installDependencies(projectDir);
547
+ if (platform === 'ios') {
548
+ await exec('pod', ['install'], {
549
+ cwd: getExpoProjectDir(projectDir) + '/ios'
550
+ });
551
+ }
552
+ await exec('npx', [
553
+ 'react-native',
554
+ platform === 'android' ? 'run-android' : 'run-ios'
555
+ ], {
556
+ cwd: getExpoProjectDir(projectDir)
557
+ });
558
+ watchProjectChanges(previewUrl, () => {
559
+ const startTime = Date.now();
560
+ syncProject()
561
+ .then(() => {
562
+ logger.info({
563
+ label: loggerLabel,
564
+ message: `Sync Time: ${(Date.now() - startTime)/ 1000}s.`
565
+ });
566
+ })
567
+ .then(() => transpile(projectDir, previewUrl, true))
568
+ .then(() => {
569
+ logger.info({
570
+ label: loggerLabel,
571
+ message: `Total Time: ${(Date.now() - startTime)/ 1000}s.`
572
+ });
573
+ });
574
+ });
575
+ watchForPlatformChanges(() => transpile(projectDir, previewUrl, false));
576
+ } catch(e) {
577
+ logger.error({
578
+ label: loggerLabel,
579
+ message: e
580
+ });
581
+ }
582
+ }
583
+
584
+ module.exports = {
585
+ runESBuildWebPreview: (previewUrl, clean, authToken) => {
586
+ isWebPreview = true;
587
+ runExpo(previewUrl, clean, authToken);
588
+ },
589
+ runExpo: runExpo,
590
+ runAndroid: (previewUrl, clean) => runNative(previewUrl, 'android', clean),
591
+ runIos: (previewUrl, clean) => runNative(previewUrl, 'ios', clean),
592
+ sync: (previewUrl, clean, _useProxy) => {
593
+ useProxy = _useProxy;
594
+ return sync(previewUrl, clean)
595
+ }
596
+ };