@wavemaker/rn-codegen 11.13.0-rc.6255 → 11.14.0-rc.232

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.
@@ -79,9 +79,9 @@ const ALLOWED_RESOURCE_EXT = [
79
79
  ];
80
80
  const pluginOperationConfig = [];
81
81
  const operationMap = {
82
- 'captureImage': { name: 'Camera', method: 'CaptureImageOperation', filename: 'capture-image', hasConstructorParams: true, packages: ['expo-camera', 'expo-file-system'], tagName: ['WmCamera', 'WmBarcodescanner'], removePlugin: true },
83
- 'captureVideo': { name: 'Camera', method: 'CaptureVideoOperation', filename: 'capture-video', hasConstructorParams: true, packages: ['expo-camera', 'expo-file-system'], tagName: ['WmCamera', 'WmBarcodescanner'], removePlugin: true },
84
- 'scanBarCode': { name: 'Scan', method: 'ScanOperation', filename: 'scan', hasConstructorParams: true, packages: [], tagName: ['WmBarcodescanner'], removePlugin: true },
82
+ 'captureImage': { name: 'Camera', method: 'CaptureImageOperation', filename: 'capture-image', hasConstructorParams: true, packages: ['expo-camera', 'expo-file-system', 'expo-av'], tagName: ['WmCamera', 'WmBarcodescanner'], removePlugin: true },
83
+ 'captureVideo': { name: 'Camera', method: 'CaptureVideoOperation', filename: 'capture-video', hasConstructorParams: true, packages: ['expo-camera', 'expo-file-system', 'expo-av'], tagName: ['WmCamera', 'WmBarcodescanner'], removePlugin: true },
84
+ 'scanBarCode': { name: 'Scan', method: 'ScanOperation', filename: 'scan', hasConstructorParams: true, packages: ['expo-camera'], tagName: ['WmBarcodescanner'], removePlugin: true },
85
85
  'getAppInfo': { name: 'Device', method: 'AppInfoOperation', filename: 'app-info', hasConstructorParams: false, packages: [], tagName: [], removePlugin: false },
86
86
  'getDeviceInfo': { name: 'Device', method: 'DeviceInfoOperation', filename: 'device-info', hasConstructorParams: false, packages: ['expo-device'], tagName: [], removePlugin: false },
87
87
  'getNetworkInfo': { name: 'Device', method: 'NetworkInfoOperation', filename: 'network-info', hasConstructorParams: false, packages: ['expo-network', '@react-native-community/netinfo'], removePlugin: false },
@@ -92,9 +92,9 @@ const operationMap = {
92
92
  'createEvent': { name: 'Calendar', method: 'CreateEventOperation', filename: 'create-event', hasConstructorParams: true, packages: ['expo-calendar'], tagName: [], removePlugin: true },
93
93
  'deleteEvent': { name: 'Calendar', method: 'DeleteEventOperation', filename: 'delete-event', hasConstructorParams: true, packages: ['expo-calendar'], tagName: [], removePlugin: true },
94
94
  'upload': { name: 'File', method: 'UploadFileOperation', filename: 'upload-file', hasConstructorParams: false, packages: ['expo-document-picker'], tagName: ['WmFileupload'], removePlugin: true },
95
- 'playAudio': { name: 'Audio', method: 'PlayAudioOperation', filename: '', hasConstructorParams: false, packages: ['expo-av'], tagName: ['WmAudio', 'WmCamera'], removePlugin: true },
95
+ 'playAudio': { name: 'Audio', method: 'PlayAudioOperation', filename: '', hasConstructorParams: false, packages: ['expo-av'], tagName: ['WmAudio'], removePlugin: true },
96
96
  'playVideo': { name: 'Video', method: 'PlayVideoOperation', filename: '', hasConstructorParams: false, packages: ['expo-video'], tagName: ['WmVideo'], removePlugin: true },
97
- 'openFile': { name: 'File', method: 'OpenFileOperation', filename: 'open-file', hasConstructorParams: false, packages: ['expo-sharing', 'expo-file-system'], tagName: ['OpenFileOperation'], removePlugin: true },
97
+ 'openFile': { name: 'File', method: 'OpenFileOperation', filename: 'open-file', hasConstructorParams: false, packages: ['expo-sharing', 'expo-file-system'], tagName: [], removePlugin: true },
98
98
  };
99
99
  const serviceParams = {
100
100
  'Camera': [{
@@ -135,6 +135,7 @@ class AppGenerator {
135
135
  this.pluginsInApp = {};
136
136
  this.wmxComponents = [];
137
137
  this.jsFileImports = new Set();
138
+ this.shouldNpmInstall = false;
138
139
  this.options = options;
139
140
  this.isPrefabApp = !!prefabName;
140
141
  this.wmxComponents = this.projectService.getWMXComponents();
@@ -174,42 +175,76 @@ class AppGenerator {
174
175
  * @description Get the list of packages (from operationMap) that are used in the generated app
175
176
  * @returns packagesInAppObj: Record<string, boolean>
176
177
  */
177
- getListOfPackagesUsedInGeneratedApp() {
178
- var _a;
178
+ async getListOfPackagesUsedInGeneratedApp() {
179
+ var _a, _b;
179
180
  const operations = (0, lodash_1.uniq)((_a = this.projectPackageDependencies) === null || _a === void 0 ? void 0 : _a.map((p) => p.name));
180
181
  const packagesInAppObj = {};
181
- operations.forEach((operation) => {
182
- var _a;
182
+ // common device operations present in all WaveMaker apps (e.g, getAppInfo, getDeviceInfo, getNetworkInfo)
183
+ const commonPluginsUsedInApp = ((_b = this.projectPackageDependencies.filter((p) => !p.removePlugin)) === null || _b === void 0 ? void 0 : _b.length) > 0;
184
+ /**
185
+ * find all operations/packages (e.g, Camera, Scan, Location)
186
+ * that are used in the app
187
+ */
188
+ await Promise.all(operations.map(async (operation) => {
183
189
  let method = "";
184
- const hasPluginInApp = ((_a = this.projectPackageDependencies.filter((p) => p.name === operation && !p.removePlugin)) === null || _a === void 0 ? void 0 : _a.length) > 0;
185
- const filteredOperations = pluginOperationConfig === null || pluginOperationConfig === void 0 ? void 0 : pluginOperationConfig.filter((config) => {
186
- method = config.method;
190
+ const importedWidgets = await this.getWidgetImports();
191
+ const deviceVariables = await this.getDeviceVariables();
192
+ const isWidgetImportedForCurrentOperation = importedWidgets.findIndex((w) => {
193
+ const widgetName = operation === "File"
194
+ ? "WmFileupload"
195
+ : operation === "Scan"
196
+ ? "WmBarcodescanner"
197
+ : `Wm${operation}`;
198
+ return w.name === widgetName;
199
+ }) !== -1;
200
+ // other device operations used in the app
201
+ // that can be removed if unused (e.g, captureImage, scanBarCode)
202
+ const deviceOperationsUsedInApp = deviceVariables === null || deviceVariables === void 0 ? void 0 : deviceVariables.filter((config) => {
203
+ if (config.type === operation) {
204
+ method = config.method;
205
+ }
187
206
  return config.type === operation;
188
207
  });
189
- const hasOperationInApp = filteredOperations.length > 0;
190
- if (operation !== 'File') {
191
- packagesInAppObj[`has${operation}Plugin`] = hasPluginInApp || hasOperationInApp;
192
- }
193
- else {
194
- if (filteredOperations.length > 1) {
195
- packagesInAppObj['hasUploadFilePlugin'] = true;
196
- packagesInAppObj['hasOpenFilePlugin'] = true;
197
- }
198
- else {
199
- if (method === 'UploadFileOperation') {
208
+ // operation is used if it's widget import is found or the device operation is found
209
+ const isCurrentOperationUsedInApp = isWidgetImportedForCurrentOperation || deviceOperationsUsedInApp.length > 0;
210
+ switch (operation) {
211
+ case 'Device':
212
+ packagesInAppObj['hasDevicePlugin'] = commonPluginsUsedInApp;
213
+ break;
214
+ case 'Camera':
215
+ case 'Scan':
216
+ case 'Location':
217
+ case 'Contacts':
218
+ case 'Calendar':
219
+ case 'Audio':
220
+ case 'Video':
221
+ packagesInAppObj[`has${operation}Plugin`] = isCurrentOperationUsedInApp;
222
+ break;
223
+ case 'File':
224
+ if (deviceOperationsUsedInApp.length >= 2) {
200
225
  packagesInAppObj['hasUploadFilePlugin'] = true;
201
- }
202
- else if (method === 'OpenFileOperation') {
203
226
  packagesInAppObj['hasOpenFilePlugin'] = true;
204
227
  }
205
- }
228
+ else {
229
+ if (method === 'UploadFileOperation' || (operation === 'File' && isWidgetImportedForCurrentOperation)) {
230
+ packagesInAppObj['hasUploadFilePlugin'] = true;
231
+ }
232
+ else if (method === 'OpenFileOperation') {
233
+ packagesInAppObj['hasOpenFilePlugin'] = true;
234
+ }
235
+ }
236
+ break;
237
+ default:
238
+ break;
206
239
  }
207
- });
240
+ }));
208
241
  return packagesInAppObj;
209
242
  }
210
- generatePluginsOperationConfig() {
243
+ async generatePluginsOperationConfig() {
211
244
  const servicesList = [];
212
- deviceVariables.forEach((v) => {
245
+ // * get all device variables (both app and page level) before processing
246
+ let allDeviceVariables = await this.getDeviceVariables();
247
+ allDeviceVariables.forEach((v) => {
213
248
  const hasEntry = pluginOperationConfig.find(p => p.operation === v.operation);
214
249
  if (hasEntry) {
215
250
  return false;
@@ -269,7 +304,8 @@ class AppGenerator {
269
304
  item.filePath = this.getFilePath(item.name);
270
305
  });
271
306
  // * get plugin list before generating device operation loader
272
- this.pluginsInApp = this.getListOfPackagesUsedInGeneratedApp();
307
+ this.pluginsInApp = await this.getListOfPackagesUsedInGeneratedApp();
308
+ // previously pluginOperationConfig was only having device variables from app.variables.js, not from Page level variables (e.g, Main.variables.js)
273
309
  const output = DEVICE_SERVICES_TEMPLATE({
274
310
  pluginOperationConfig: pluginOperationConfig,
275
311
  appVersion: appVersion,
@@ -286,19 +322,7 @@ class AppGenerator {
286
322
  openFilePlugin: this.pluginsInApp.hasOpenFilePlugin
287
323
  });
288
324
  (0, utils_1.writeFile)(`${this.projectPath}/src/device-operation-loader.js`, output);
289
- // * generate device plugin services & permission service
290
- const projectDependencies = this.projectPackageDependencies;
291
- projectDependencies.forEach((dep, index) => {
292
- var _a;
293
- const hasPluginOperation = ((_a = pluginOperationConfig === null || pluginOperationConfig === void 0 ? void 0 : pluginOperationConfig.filter((config) => {
294
- var _a;
295
- return `Wm${config.type}` === ((_a = dep === null || dep === void 0 ? void 0 : dep.tagName) === null || _a === void 0 ? void 0 : _a.includes(config.type));
296
- })) === null || _a === void 0 ? void 0 : _a.length) > 0;
297
- if (hasPluginOperation) {
298
- projectDependencies[index].removePlugin = false;
299
- }
300
- });
301
- this.projectPackageDependencies = projectDependencies;
325
+ // * generate device plugin services, permission service & plugin provider
302
326
  const pluginServices = DEVICE_PLUGIN_SERVICES_TEMPLATE({
303
327
  expoCameraPlugin: this.pluginsInApp.hasCameraPlugin,
304
328
  scanPlugin: this.pluginsInApp.hasScanPlugin,
@@ -476,16 +500,17 @@ class AppGenerator {
476
500
  appConfig['expo']['jsEngine'] = ((_a = config.preferences) === null || _a === void 0 ? void 0 : _a.enableHermes) === 'false' ? 'jsc' : 'hermes';
477
501
  appConfig['expo']['icon'] = './assets/' + config.icon.src;
478
502
  appConfig['expo']['android']['adaptiveIcon']['foregroundImage'] = './assets/' + config.icon.src;
503
+ appConfig['expo']['orientation'] = config.orientation || 'portrait';
479
504
  this.updateInSplashConfig(config, appConfig);
480
505
  }).then(() => {
481
506
  return this.projectService.getAppJSON()
482
507
  .then(appConfigOverride => {
483
- var _a;
484
- (_a = appConfigOverride === null || appConfigOverride === void 0 ? void 0 : appConfigOverride.expo) === null || _a === void 0 ? void 0 : _a.plugins.forEach((plugin) => {
508
+ var _a, _b;
509
+ (_b = (_a = appConfigOverride === null || appConfigOverride === void 0 ? void 0 : appConfigOverride.expo) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.forEach((plugin) => {
485
510
  var _a, _b;
486
511
  if ((_a = appConfig === null || appConfig === void 0 ? void 0 : appConfig.expo) === null || _a === void 0 ? void 0 : _a.plugins) {
487
512
  const presentInIndex = appConfig.expo.plugins.findIndex((item) => item[0] === plugin[0]);
488
- if (presentInIndex > -1) {
513
+ if (presentInIndex > -1 && Array.isArray(appConfig.expo.plugins[presentInIndex])) {
489
514
  appConfig.expo.plugins[presentInIndex][1] = plugin[1];
490
515
  }
491
516
  else {
@@ -527,7 +552,7 @@ class AppGenerator {
527
552
  v.publicKeyHashes.push("NoBackUpKey" + v.publicKeyHashes[0].substring(11));
528
553
  }
529
554
  });
530
- const enableWavePulse = (!!config.preferences.enableWavePulse);
555
+ const enableWavePulse = (!!config.preferences.enableWavePulse || profile_1.default.targetPlatform === 'web');
531
556
  if (!enableWavePulse) {
532
557
  if (fs_extra_1.default.existsSync(`${this.projectPath}/wavepulse`)) {
533
558
  fs_extra_1.default.rmSync(`${this.projectPath}/wavepulse`, {
@@ -633,18 +658,6 @@ class AppGenerator {
633
658
  });
634
659
  }
635
660
  const imports = (0, lodash_1.sortBy)((0, lodash_1.uniqBy)(transpiledOutput.imports, i => i.name + i.from), i => i.name);
636
- const projectDependencies = (0, lodash_1.uniq)([...this.projectPackageDependencies]);
637
- projectDependencies.forEach((dep, index) => {
638
- const isWmTagImported = imports.findIndex((i) => {
639
- var _a;
640
- const hasTagName = (_a = dep.tagName) === null || _a === void 0 ? void 0 : _a.includes(i === null || i === void 0 ? void 0 : i.name);
641
- return hasTagName;
642
- }) >= 0;
643
- if (isWmTagImported && projectDependencies[index]) {
644
- projectDependencies[index].removePlugin = false;
645
- }
646
- });
647
- this.projectPackageDependencies = projectDependencies;
648
661
  const component = COMPONENT_TEMPLATE({
649
662
  name: name,
650
663
  type: type,
@@ -868,7 +881,8 @@ class AppGenerator {
868
881
  encoding: 'utf8'
869
882
  });
870
883
  const output = BABEL_CONFIG_TEMPLATE({
871
- enableLogs: config.preferences.enableLogs
884
+ enableLogs: config.preferences.enableLogs,
885
+ isWeb: profile_1.default.targetPlatform === 'web'
872
886
  });
873
887
  (0, utils_1.writeFile)(`${this.projectPath}/babel.config.js`, output);
874
888
  }
@@ -922,11 +936,110 @@ class AppGenerator {
922
936
  }
923
937
  return arrayOfFiles;
924
938
  }
939
+ /**
940
+ * @description Get all widget imports in the generated app
941
+ * @returns array of widgets imported (e.g, WmCamera, WmVideo)
942
+ */
943
+ async getWidgetImports() {
944
+ let widgetImports = [];
945
+ const pageConfigs = await this.projectService.getPageConfigs();
946
+ await Promise.all(pageConfigs.map(async (p) => {
947
+ const info = await this.projectService.getPageInfo(p.name);
948
+ const transpileArgs = {
949
+ markup: info.markup,
950
+ isPartOfPrefab: this.isPrefabApp,
951
+ rnConfig: this.rnConfig,
952
+ };
953
+ const transpiledOutput = (0, transpile_1.transpileMarkup)(transpileArgs);
954
+ if (info.markup.includes('metadata')) {
955
+ if (this.isPrefabApp) {
956
+ transpiledOutput.imports.push({ name: '{dynamicForm}', from: `../../../../../../component/dynamic/form.generator` });
957
+ }
958
+ else {
959
+ transpiledOutput.imports.push({ name: '{dynamicForm}', from: `../../../component/dynamic/form.generator` });
960
+ }
961
+ }
962
+ if (this.isPrefabApp) {
963
+ transpiledOutput.imports.forEach(i => {
964
+ if (i.from.indexOf('/prefabs/') > 0) {
965
+ i.from = '../../../' + i.from;
966
+ }
967
+ });
968
+ }
969
+ const uniqueImports = (0, lodash_1.sortBy)((0, lodash_1.uniqBy)(transpiledOutput.imports, i => i.name + i.from), i => i.name);
970
+ if (Array.isArray(uniqueImports) && uniqueImports.length > 0) {
971
+ widgetImports.push(...uniqueImports);
972
+ }
973
+ }));
974
+ const uniqueWidgetImports = (0, lodash_1.uniq)(widgetImports);
975
+ return uniqueWidgetImports;
976
+ }
977
+ /**
978
+ * Check for widget usage (e.g, WmCamera, WmVideo)
979
+ * in component files (page, partial, prefab) of the generated app
980
+ * and remove unused packages accordingly
981
+ */
982
+ async checkWidgetUsageInComponentFiles() {
983
+ if (this.incBuilder) {
984
+ console.log(`Checking widget usage in component files.`);
985
+ }
986
+ const projectDependencies = (0, lodash_1.uniq)([...this.projectPackageDependencies]);
987
+ const imports = await this.getWidgetImports();
988
+ projectDependencies.forEach((dep, index) => {
989
+ const isWmTagImported = imports.findIndex((i) => {
990
+ var _a;
991
+ const hasTagName = (_a = dep.tagName) === null || _a === void 0 ? void 0 : _a.includes(i === null || i === void 0 ? void 0 : i.name);
992
+ return hasTagName;
993
+ }) >= 0;
994
+ if (isWmTagImported && projectDependencies[index]) {
995
+ projectDependencies[index].removePlugin = false;
996
+ }
997
+ });
998
+ this.projectPackageDependencies = projectDependencies;
999
+ }
1000
+ /**
1001
+ * @description Get the list of device variables used
1002
+ * in the app (App + Page owner)
1003
+ * @returns deviceVariables: any[]
1004
+ */
1005
+ async getDeviceVariables() {
1006
+ let deviceVariables = [];
1007
+ let allPagesDeviceVariables = [];
1008
+ const pageConfigs = await this.projectService.getPageConfigs();
1009
+ const appVariables = await this.projectService.getAppVariables();
1010
+ const appDeviceVariables = Object.values(appVariables).filter((v) => v.category === 'wm.DeviceVariable');
1011
+ await Promise.all(pageConfigs.map(async (p) => {
1012
+ const info = await this.projectService.getPageInfo(p.name);
1013
+ const pageVariables = JSON.parse(info.variables);
1014
+ const pageDeviceVariables = Object.values(pageVariables).filter((v) => v.category === 'wm.DeviceVariable');
1015
+ allPagesDeviceVariables.push(...pageDeviceVariables);
1016
+ }));
1017
+ const allDeviceVariables = [...appDeviceVariables, ...allPagesDeviceVariables];
1018
+ allDeviceVariables.forEach((v) => {
1019
+ const selectedOperation = operationMap[v.operation];
1020
+ if (!selectedOperation) {
1021
+ console.log('Cannot find operation named ' + v.operation);
1022
+ }
1023
+ if (deviceVariables.find((dv) => dv.operation === v.operation)) {
1024
+ return;
1025
+ }
1026
+ deviceVariables.push({
1027
+ 'operation': v.operation,
1028
+ 'service': v.service,
1029
+ 'filename': selectedOperation.filename,
1030
+ 'method': selectedOperation.method,
1031
+ 'hasparams': selectedOperation.hasConstructorParams,
1032
+ 'type': selectedOperation.name
1033
+ });
1034
+ });
1035
+ return deviceVariables;
1036
+ }
925
1037
  /**
926
1038
  * Scan all JS files in the project for imports that match packages in operationMap
927
1039
  */
928
1040
  scanJSFilesForImports() {
929
1041
  this.jsFileImports.clear();
1042
+ const scanFileIgnoreList = ['device-operation-loader.js', 'device-permission-service.js', 'device-plugin-service.js', 'plugin-provider.js'];
930
1043
  // Get all packages from operationMap
931
1044
  const allOperationPackages = new Set();
932
1045
  Object.values(operationMap).forEach((operation) => {
@@ -946,6 +1059,10 @@ class AppGenerator {
946
1059
  }
947
1060
  else if (file.endsWith('.js') || file.endsWith('.ts') || file.endsWith('.tsx')) {
948
1061
  try {
1062
+ // Ignore checking for package imports in ignore list file names
1063
+ if (scanFileIgnoreList.includes(file)) {
1064
+ return;
1065
+ }
949
1066
  const content = fs_extra_1.default.readFileSync(fullPath, 'utf-8');
950
1067
  // Look for import statements that match operationMap packages
951
1068
  allOperationPackages.forEach(packageName => {
@@ -977,7 +1094,16 @@ class AppGenerator {
977
1094
  scanPaths.forEach(scanPath => {
978
1095
  scanDirectory(scanPath);
979
1096
  });
980
- console.log(`Found imports for packages: ${Array.from(this.jsFileImports).join(', ')}`);
1097
+ if (this.jsFileImports.size > 0) {
1098
+ console.log(`Found imports for packages in JS files: ${Array.from(this.jsFileImports).join(', ')}`);
1099
+ }
1100
+ }
1101
+ checkPackageUsageInJSFiles() {
1102
+ if (this.incBuilder) {
1103
+ console.log(`Checking package usage in JS files.`);
1104
+ }
1105
+ // Scan JS files for imports before processing package.json
1106
+ this.scanJSFilesForImports();
981
1107
  }
982
1108
  generateFontConfig() {
983
1109
  if (this.incBuilder) {
@@ -1089,39 +1215,129 @@ class AppGenerator {
1089
1215
  fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(packageOverriden, null, 4));
1090
1216
  }
1091
1217
  }
1218
+ /**
1219
+ * update app.json with expo-video plugin
1220
+ * @param updatedPackageJson
1221
+ */
1222
+ async updateAppJsonWithExpoVideoPlugin(updatedPackageJson) {
1223
+ var _a, _b, _c;
1224
+ const appJson = fs_extra_1.default.readJsonSync(`${this.projectPath}/app.json`, { encoding: 'utf-8' });
1225
+ const hasExpoVideoPackage = !!((_a = updatedPackageJson === null || updatedPackageJson === void 0 ? void 0 : updatedPackageJson.dependencies) === null || _a === void 0 ? void 0 : _a['expo-video']);
1226
+ const customAppJson = await this.projectService.getAppJSON();
1227
+ const customAppJsonExpoVideoPlugin = (_c = (_b = customAppJson === null || customAppJson === void 0 ? void 0 : customAppJson.expo) === null || _b === void 0 ? void 0 : _b.plugins) === null || _c === void 0 ? void 0 : _c.filter((plugin) => Array.isArray(plugin) && plugin[0] === 'expo-video' || typeof plugin === 'string' && plugin === 'expo-video');
1228
+ const hasExpoVideoPluginInCustomAppJson = (customAppJsonExpoVideoPlugin === null || customAppJsonExpoVideoPlugin === void 0 ? void 0 : customAppJsonExpoVideoPlugin.length) > 0;
1229
+ const expoVideoPluginEntry = ["expo-video", { "supportsBackgroundPlayback": true, "supportsPictureInPicture": true }];
1230
+ const pluginExists = appJson.expo.plugins.some((plugin) => {
1231
+ return Array.isArray(plugin) && plugin[0] === "expo-video" || typeof plugin === 'string' && plugin === 'expo-video';
1232
+ });
1233
+ if (pluginExists) {
1234
+ return;
1235
+ }
1236
+ if (hasExpoVideoPackage && !hasExpoVideoPluginInCustomAppJson) {
1237
+ // keep original expo-video plugin array
1238
+ appJson.expo.plugins.push(expoVideoPluginEntry);
1239
+ (0, utils_1.writeFile)(`${this.projectPath}/app.json`, JSON.stringify(appJson, null, 4));
1240
+ }
1241
+ else if (!hasExpoVideoPackage && hasExpoVideoPluginInCustomAppJson) {
1242
+ // keep expo-video plugin array from custom app.json
1243
+ appJson.expo.plugins.push(...customAppJsonExpoVideoPlugin);
1244
+ (0, utils_1.writeFile)(`${this.projectPath}/app.json`, JSON.stringify(appJson, null, 4));
1245
+ }
1246
+ else if (hasExpoVideoPackage && hasExpoVideoPluginInCustomAppJson) {
1247
+ // keep expo-video plugin array from custom app.json
1248
+ appJson.expo.plugins.push(...customAppJsonExpoVideoPlugin);
1249
+ (0, utils_1.writeFile)(`${this.projectPath}/app.json`, JSON.stringify(appJson, null, 4));
1250
+ }
1251
+ }
1092
1252
  /**
1093
1253
  * remove unused dependencies from package.json
1094
1254
  * @param packageJsonData
1095
1255
  * @returns updatedPackageJson, pluginsRemoved
1096
1256
  */
1097
1257
  async removeUnusedPluginsFromPackageJson(packageJsonData) {
1258
+ var _a;
1098
1259
  let updatedPackageJson = (0, lodash_1.cloneDeep)(packageJsonData);
1099
1260
  let pluginsRemoved = false;
1261
+ let removedPackages = [];
1100
1262
  try {
1101
1263
  const dependencies = (0, lodash_1.cloneDeep)(this.projectPackageDependencies);
1264
+ const listOfPackagesUsed = await this.getListOfPackagesUsedInGeneratedApp();
1265
+ const operationsUsed = Object.entries(listOfPackagesUsed)
1266
+ .map(([key, value]) => {
1267
+ if (value) {
1268
+ return key.replace(/^has/, "").replace(/Plugin$/, "");
1269
+ }
1270
+ })
1271
+ .filter((value) => value !== undefined);
1272
+ // iterating over each operation of the operationMap
1102
1273
  for (let i = 0; i < dependencies.length; i++) {
1103
1274
  const removePlugin = dependencies[i].removePlugin;
1104
- if (removePlugin) {
1105
- // Check if any packages from this operation are imported in JS files
1106
- const hasImportsInJSFiles = dependencies[i].packages.some((pkg) => this.jsFileImports.has(pkg));
1107
- // Only remove packages if they're not imported in JS files
1108
- if (!hasImportsInJSFiles) {
1109
- dependencies[i].packages.forEach((p) => {
1110
- delete updatedPackageJson.dependencies[p];
1111
- });
1112
- pluginsRemoved = true;
1113
- }
1114
- else {
1115
- console.log(`Keeping packages ${dependencies[i].packages.join(', ')} due to imports found in JS files`);
1116
- }
1275
+ const deviceOperationName = dependencies[i].name;
1276
+ const deviceOperationMethod = dependencies[i].name === 'File' ? (_a = dependencies[i].method) === null || _a === void 0 ? void 0 : _a.replace('Operation', '') : dependencies[i].name;
1277
+ const operationFound = operationsUsed.includes(deviceOperationMethod);
1278
+ const currentServiceUsedByOtherOperation = dependencies
1279
+ .filter((dep) => { var _a; return dep.name !== deviceOperationName && ((_a = dep.packages) === null || _a === void 0 ? void 0 : _a.some((p) => dependencies[i].packages.includes(p))); }) // other operations that are using at least one of the packages of the current operation
1280
+ .filter((dep) => {
1281
+ var _a;
1282
+ const depName = dep.name === 'File' ? (_a = dep.method) === null || _a === void 0 ? void 0 : _a.replace('Operation', '') : dep.name;
1283
+ return operationsUsed.includes(depName);
1284
+ });
1285
+ // Check if any packages from this operation are imported in JS files
1286
+ const hasImportsInJSFiles = dependencies[i].packages.some((pkg) => this.jsFileImports.has(pkg));
1287
+ /**
1288
+ * Remove package if:
1289
+ * 1. removePlugin is true
1290
+ * 2. the current operation is not used in the app
1291
+ * 3. the packages of the current operation are not imported in JS files
1292
+ */
1293
+ const removePackage = removePlugin && !operationFound && !hasImportsInJSFiles;
1294
+ // console.log("<---Remove current package--->", "\ndependencies[i].method:", dependencies[i].method, "\nremovePackage:", removePackage, "\nremovePlugin:", removePlugin, "\noperationFound:", operationFound, "\nhasImportsInJSFiles:", hasImportsInJSFiles);
1295
+ if (removePackage) {
1296
+ dependencies[i].packages.forEach((p) => {
1297
+ // whether this specific package of the current operation is used by other operations
1298
+ const isPackageUsedByOtherOperation = currentServiceUsedByOtherOperation.filter((operation) => operation.packages.includes(p)).length > 0;
1299
+ if (isPackageUsedByOtherOperation) {
1300
+ return;
1301
+ }
1302
+ delete updatedPackageJson.dependencies[p];
1303
+ if (!removedPackages.includes(p)) {
1304
+ removedPackages.push(p);
1305
+ }
1306
+ });
1307
+ pluginsRemoved = true;
1308
+ }
1309
+ if (hasImportsInJSFiles) {
1310
+ console.log(`Keeping packages ${dependencies[i].packages.join(', ')} due to imports found in JS files`);
1117
1311
  }
1118
1312
  }
1119
1313
  }
1120
1314
  catch (error) {
1121
1315
  console.log('Failed to remove unused plugins from package json: ', error);
1122
1316
  }
1317
+ await this.updateAppJsonWithExpoVideoPlugin(updatedPackageJson);
1318
+ console.log(`Removed the following unused packages: ${removedPackages.join(', ')}`);
1123
1319
  return { updatedPackageJson, pluginsRemoved };
1124
1320
  }
1321
+ async removeUnusedPackages() {
1322
+ if (this.incBuilder) {
1323
+ console.log(`Removing unused packages.`);
1324
+ }
1325
+ let packageJsonToUpdate = fs_extra_1.default.readJSONSync(`${this.projectPath}/package.json`, { encoding: 'utf-8' });
1326
+ const templatePackageJson = fs_extra_1.default.readJSONSync(`${__dirname}/templates/project/package.json`, { encoding: 'utf-8' });
1327
+ const templateDependencies = templatePackageJson.dependencies;
1328
+ // * template dependencies + packages from wm_rn_config.json's plugins
1329
+ const mergedDependencies = (0, lodash_1.merge)(packageJsonToUpdate.dependencies, templateDependencies);
1330
+ packageJsonToUpdate.dependencies = mergedDependencies;
1331
+ const { updatedPackageJson, pluginsRemoved } = await this.removeUnusedPluginsFromPackageJson(packageJsonToUpdate);
1332
+ if (pluginsRemoved) {
1333
+ this.shouldNpmInstall = true;
1334
+ }
1335
+ fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(updatedPackageJson, null, 4));
1336
+ // * installing packages whenever a package is added or removed
1337
+ if (this.shouldNpmInstall && this.incBuilder) {
1338
+ (0, command_1.npmInstall)(`${this.projectPath}`);
1339
+ }
1340
+ }
1125
1341
  async copyPackageLockJson() {
1126
1342
  if (fs_extra_1.default.existsSync(`${this.projectPath}/package-lock.json`)) {
1127
1343
  return;
@@ -1200,8 +1416,6 @@ class AppGenerator {
1200
1416
  if (this.incBuilder) {
1201
1417
  console.log(`Adding custom plugins.`);
1202
1418
  }
1203
- // Scan JS files for imports before processing package.json
1204
- this.scanJSFilesForImports();
1205
1419
  await this.projectService.getExpoPlugins().then(src => {
1206
1420
  if (src) {
1207
1421
  fs_extra_1.default.mkdirsSync(`${this.projectPath}/expo-plugins`);
@@ -1250,11 +1464,10 @@ class AppGenerator {
1250
1464
  }
1251
1465
  }
1252
1466
  });
1253
- const { updatedPackageJson, pluginsRemoved } = await this.removeUnusedPluginsFromPackageJson(jsonData);
1254
- if (pluginsRemoved) {
1255
- shouldNpmInstall = true;
1467
+ if (shouldNpmInstall) {
1468
+ this.shouldNpmInstall = true;
1256
1469
  }
1257
- fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(updatedPackageJson, null, 4));
1470
+ fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(jsonData, null, 4));
1258
1471
  });
1259
1472
  }
1260
1473
  else {
@@ -1274,15 +1487,10 @@ class AppGenerator {
1274
1487
  }
1275
1488
  }
1276
1489
  });
1277
- const { updatedPackageJson, pluginsRemoved } = await this.removeUnusedPluginsFromPackageJson(jsonData);
1278
- if (pluginsRemoved) {
1279
- shouldNpmInstall = true;
1490
+ if (shouldNpmInstall) {
1491
+ this.shouldNpmInstall = true;
1280
1492
  }
1281
- fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(updatedPackageJson, null, 4));
1282
- }
1283
- // * installing packages whenever a plugin is added or removed
1284
- if (shouldNpmInstall && this.incBuilder) {
1285
- (0, command_1.npmInstall)(`${this.projectPath}`);
1493
+ fs_extra_1.default.writeFileSync(`${this.projectPath}/package.json`, JSON.stringify(jsonData, null, 4));
1286
1494
  }
1287
1495
  }
1288
1496
  async generateAppTheme() {
@@ -1404,15 +1612,18 @@ class AppGenerator {
1404
1612
  return Promise.all(components.map(c => this.generateWMXComponents(c, true)));
1405
1613
  }
1406
1614
  }).then(() => !this.incBuilder)
1407
- .then((build) => (build && this.prepareDeviceOperationLoader()))
1615
+ .then((build) => (this.prepareDeviceOperationLoader()))
1408
1616
  .then(() => !this.incBuilder
1409
1617
  || this.incBuilder.isConfigModified()
1410
1618
  || this.incBuilder.isPrefabModified())
1411
1619
  .then((build) => (build && this.addCustomPluginsToPackageJson()))
1412
- .then(() => !this.incBuilder)
1413
- .then((build) => (build && this.prettify()))
1414
1620
  .then(() => this.mergePackageJson())
1415
1621
  .then(() => this.copyPackageLockJson())
1622
+ .then(() => this.checkWidgetUsageInComponentFiles())
1623
+ .then(() => this.checkPackageUsageInJSFiles())
1624
+ .then(() => this.removeUnusedPackages())
1625
+ .then(() => !this.incBuilder)
1626
+ .then((build) => (build && this.prettify()))
1416
1627
  .then(() => this.projectService.copyNpmPackages(this.projectPath))
1417
1628
  .then(() => this.copyScreenCapturePluginIfNeeded())
1418
1629
  .then(() => console.log('code generated at ' + this.projectPath));