nativescript 9.1.0-alpha.2 → 9.1.0-alpha.3

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.
@@ -22,6 +22,11 @@ class PrepareCommand extends command_base_1.ValidatePlatformCommandBase {
22
22
  hasSensitiveValue: false,
23
23
  },
24
24
  hmr: { type: "boolean" /* OptionType.Boolean */, default: false, hasSensitiveValue: false },
25
+ skipNative: {
26
+ type: "boolean" /* OptionType.Boolean */,
27
+ default: false,
28
+ hasSensitiveValue: false,
29
+ },
25
30
  whatever: {
26
31
  type: "boolean" /* OptionType.Boolean */,
27
32
  default: false,
@@ -15,7 +15,7 @@ const util = require("util");
15
15
  const _ = require("lodash");
16
16
  const yok_1 = require("../common/yok");
17
17
  class RunController extends events_1.EventEmitter {
18
- constructor($analyticsService, $buildController, $debugController, $deviceInstallAppService, $devicesService, $errors, $injector, $hmrStatusService, $hooksService, $liveSyncServiceResolver, $liveSyncProcessDataService, $logger, $mobileHelper, $platformsDataService, $pluginsService, $prepareController, $prepareDataService, $prepareNativePlatformService, $projectChangesService, $projectDataService) {
18
+ constructor($analyticsService, $buildController, $debugController, $deviceInstallAppService, $devicesService, $errors, $injector, $hmrStatusService, $hooksService, $liveSyncServiceResolver, $liveSyncProcessDataService, $logger, $mobileHelper, $platformsDataService, $pluginsService, $prepareController, $prepareDataService, $prepareNativePlatformService, $projectChangesService, $projectDataService, $staticConfig) {
19
19
  super();
20
20
  this.$analyticsService = $analyticsService;
21
21
  this.$buildController = $buildController;
@@ -37,7 +37,10 @@ class RunController extends events_1.EventEmitter {
37
37
  this.$prepareNativePlatformService = $prepareNativePlatformService;
38
38
  this.$projectChangesService = $projectChangesService;
39
39
  this.$projectDataService = $projectDataService;
40
+ this.$staticConfig = $staticConfig;
40
41
  this.prepareReadyEventHandler = null;
42
+ this._syncInProgress = false;
43
+ this._pendingSyncs = new Map();
41
44
  }
42
45
  async run(runData) {
43
46
  const { liveSyncInfo, deviceDescriptors } = runData;
@@ -60,13 +63,11 @@ class RunController extends events_1.EventEmitter {
60
63
  const platformData = this.$platformsDataService.getPlatformData(data.platform, projectData);
61
64
  const prepareData = this.$prepareDataService.getPrepareData(liveSyncInfo.projectDir, data.platform, { ...liveSyncInfo, watch: !liveSyncInfo.skipWatcher });
62
65
  const changesInfo = await this.$projectChangesService.checkForChanges(platformData, projectData, prepareData);
63
- if (changesInfo.hasChanges) {
64
- await this.syncChangedDataOnDevices(data, projectData, liveSyncInfo);
66
+ if (!changesInfo.hasChanges) {
67
+ return;
65
68
  }
66
69
  }
67
- else {
68
- await this.syncChangedDataOnDevices(data, projectData, liveSyncInfo);
69
- }
70
+ this.scheduleSyncOnDevices(data, projectData, liveSyncInfo);
70
71
  };
71
72
  this.prepareReadyEventHandler = handler.bind(this);
72
73
  this.$prepareController.on(constants_2.PREPARE_READY_EVENT_NAME, this.prepareReadyEventHandler);
@@ -277,6 +278,14 @@ class RunController extends events_1.EventEmitter {
277
278
  },
278
279
  watch: !liveSyncInfo.skipWatcher,
279
280
  });
281
+ // For Android + Vite HMR, own the `adb reverse` ourselves —
282
+ // with our SDK-resolved adb, scoped to this exact serial, and
283
+ // only after the device is up — then hand the bundler the
284
+ // result via env vars. This MUST run before `prepare` (which
285
+ // spawns the Vite bundler that inherits `process.env`) so the
286
+ // bundler trusts the tunnel instead of racing us to spawn its
287
+ // own adb during config-load. See packages/vite hardening.
288
+ await this.setupAndroidViteHmrReverse(device, projectData, liveSyncInfo);
280
289
  const prepareResultData = await this.$prepareController.prepare(prepareData);
281
290
  const buildData = {
282
291
  ...deviceDescriptor.buildData,
@@ -353,6 +362,91 @@ class RunController extends events_1.EventEmitter {
353
362
  };
354
363
  await this.addActionToChain(projectData.projectDir, () => this.$devicesService.execute(deviceAction, (device) => _.some(deviceDescriptors, (deviceDescriptor) => deviceDescriptor.identifier === device.deviceInfo.identifier)));
355
364
  }
365
+ /**
366
+ * Set up `adb reverse tcp:<port> tcp:<port>` for an Android device
367
+ * when the project bundles with Vite in HMR/watch mode, then export
368
+ * the result to the bundler subprocess via environment variables.
369
+ *
370
+ * The Vite dev-host helper prefers an ADB tunnel (device-side
371
+ * `127.0.0.1:<port>` → host) over the emulator's flaky slirp NAT
372
+ * (`10.0.2.2`). Historically the bundler tried to wire that tunnel
373
+ * itself at config-load time, racing this CLI's device discovery
374
+ * over the single global adb daemon and intermittently freezing the
375
+ * run at "Searching for devices…". The CLI is the right owner: it
376
+ * knows the exact target serial and when the device is ready, and it
377
+ * already drives a single, version-matched adb. We do the reverse
378
+ * here and signal the bundler with `NS_ADB_REVERSE_READY=1` so it
379
+ * never spawns adb on its own.
380
+ *
381
+ * Best-effort: any failure is logged at trace level and swallowed.
382
+ * The bundler then falls back to its own (now hardened) adb path, or
383
+ * ultimately to `10.0.2.2`, so a reverse hiccup never fails the run.
384
+ */
385
+ async setupAndroidViteHmrReverse(device, projectData, liveSyncInfo) {
386
+ try {
387
+ if (!this.$mobileHelper.isAndroidPlatform(device.deviceInfo.platform)) {
388
+ return;
389
+ }
390
+ if (projectData.bundler !== "vite") {
391
+ return;
392
+ }
393
+ // HMR over the tunnel only matters for a live watch session.
394
+ if (liveSyncInfo.skipWatcher || !liveSyncInfo.useHotModuleReload) {
395
+ return;
396
+ }
397
+ // Respect the user's explicit opt-out — they want the
398
+ // `10.0.2.2` / LAN path, so don't create a tunnel or claim one
399
+ // exists.
400
+ if (this.isTruthyEnvFlag(process.env.NS_HMR_NO_ADB_REVERSE)) {
401
+ return;
402
+ }
403
+ // `NS_HMR_PREFER_LAN_HOST` means the dev wants LAN routing
404
+ // (physical device over Wi-Fi); the dev-host resolver suppresses
405
+ // the adb-reverse path for it, so don't bother wiring one.
406
+ if (this.isTruthyEnvFlag(process.env.NS_HMR_PREFER_LAN_HOST)) {
407
+ return;
408
+ }
409
+ const serial = device.deviceInfo.identifier;
410
+ const port = this.getViteHmrPort();
411
+ // Safe after the `isAndroidPlatform` guard above — only Android
412
+ // devices carry the `adb` bridge.
413
+ const adb = device.adb;
414
+ // Don't `reverse` against a device whose adbd isn't accepting
415
+ // yet (emulators report a `device` transport before adbd is
416
+ // fully up). `wait-for-device` returns immediately once ready.
417
+ await adb.executeCommand(["wait-for-device"], {
418
+ deviceIdentifier: serial,
419
+ });
420
+ await adb.executeCommand(["reverse", `tcp:${port}`, `tcp:${port}`], {
421
+ deviceIdentifier: serial,
422
+ });
423
+ // Hand the exact adb the CLI used to the bundler so, in any
424
+ // fallback path, it drives the same version-matched client and
425
+ // can't trigger a server-version-mismatch daemon kill.
426
+ const adbPath = await this.$staticConfig.getAdbFilePath();
427
+ process.env.NS_ADB_PATH = adbPath;
428
+ process.env.NS_DEVICE_SERIAL = serial;
429
+ process.env.NS_ADB_REVERSE_READY = "1";
430
+ this.$logger.info(`Set up adb reverse tcp:${port} tcp:${port} for ${serial} (Vite HMR routes device-side 127.0.0.1:${port} through ADB).`);
431
+ }
432
+ catch (err) {
433
+ this.$logger.trace(`Setting up adb reverse for Vite HMR failed; leaving it to the bundler fallback. Error: ${err}`);
434
+ }
435
+ }
436
+ getViteHmrPort() {
437
+ // The Vite dev server defaults to 5173; the bundler reads the same
438
+ // default. If a project runs Vite on a different port, the dev sets
439
+ // `NS_HMR_PORT` so the CLI reverses the matching port.
440
+ const fromEnv = Number(process.env.NS_HMR_PORT);
441
+ return Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : 5173;
442
+ }
443
+ isTruthyEnvFlag(value) {
444
+ if (typeof value !== "string") {
445
+ return false;
446
+ }
447
+ const v = value.trim().toLowerCase();
448
+ return !!v && v !== "0" && v !== "false" && v !== "off" && v !== "no";
449
+ }
356
450
  async syncChangedDataOnDevices(data, projectData, liveSyncInfo) {
357
451
  const successfullySyncedMessageFormat = `Successfully synced application %s on device %s.`;
358
452
  const rebuiltInformation = {};
@@ -476,18 +570,13 @@ class RunController extends events_1.EventEmitter {
476
570
  }
477
571
  }
478
572
  catch (err) {
479
- this.$logger.warn(`Unable to apply changes for device: ${device.deviceInfo.identifier}. Error is: ${err && err.message}.`);
573
+ this.$logger.warn(`Unable to apply changes for device: ${device.deviceInfo.identifier}. Error is: ${err && err.message}. Will retry on next change.`);
480
574
  this.emitCore(constants_2.RunOnDeviceEvents.runOnDeviceError, {
481
575
  projectDir: projectData.projectDir,
482
576
  deviceIdentifier: device.deviceInfo.identifier,
483
577
  applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()],
484
578
  error: err,
485
579
  });
486
- await this.stop({
487
- projectDir: projectData.projectDir,
488
- deviceIdentifiers: [device.deviceInfo.identifier],
489
- stopOptions: { shouldAwaitAllActions: false },
490
- });
491
580
  }
492
581
  };
493
582
  await this.addActionToChain(projectData.projectDir, () => this.$devicesService.execute(deviceAction, (device) => {
@@ -498,15 +587,65 @@ class RunController extends events_1.EventEmitter {
498
587
  _.some(liveSyncProcessInfo.deviceDescriptors, (deviceDescriptor) => deviceDescriptor.identifier === device.deviceInfo.identifier));
499
588
  }));
500
589
  }
590
+ scheduleSyncOnDevices(data, projectData, liveSyncInfo) {
591
+ if (this._syncInProgress) {
592
+ const platform = data.platform;
593
+ const existing = this._pendingSyncs.get(platform);
594
+ if (existing) {
595
+ existing.data = this.mergeFilesChangeEvents(existing.data, data);
596
+ }
597
+ else {
598
+ this._pendingSyncs.set(platform, { data, projectData, liveSyncInfo });
599
+ }
600
+ return;
601
+ }
602
+ this.executeSyncOnDevices(data, projectData, liveSyncInfo);
603
+ }
604
+ async executeSyncOnDevices(data, projectData, liveSyncInfo) {
605
+ this._syncInProgress = true;
606
+ try {
607
+ await this.syncChangedDataOnDevices(data, projectData, liveSyncInfo);
608
+ }
609
+ catch (err) {
610
+ this.$logger.trace(`Error during sync on devices: ${err.message || err}`);
611
+ }
612
+ finally {
613
+ const nextEntry = this._pendingSyncs.entries().next();
614
+ if (!nextEntry.done) {
615
+ const [platform, pending] = nextEntry.value;
616
+ this._pendingSyncs.delete(platform);
617
+ this.executeSyncOnDevices(pending.data, pending.projectData, pending.liveSyncInfo);
618
+ }
619
+ else {
620
+ this._syncInProgress = false;
621
+ }
622
+ }
623
+ }
624
+ mergeFilesChangeEvents(a, b) {
625
+ return {
626
+ files: [...new Set([...a.files, ...b.files])],
627
+ staleFiles: [
628
+ ...new Set([...(a.staleFiles || []), ...(b.staleFiles || [])]),
629
+ ],
630
+ hasOnlyHotUpdateFiles: a.hasOnlyHotUpdateFiles && b.hasOnlyHotUpdateFiles,
631
+ hasNativeChanges: a.hasNativeChanges || b.hasNativeChanges,
632
+ hmrData: b.hmrData,
633
+ platform: b.platform,
634
+ };
635
+ }
501
636
  async addActionToChain(projectDir, action) {
502
637
  const liveSyncInfo = this.$liveSyncProcessDataService.getPersistedData(projectDir);
503
638
  if (liveSyncInfo) {
504
- liveSyncInfo.actionsChain = liveSyncInfo.actionsChain.then(async () => {
639
+ liveSyncInfo.actionsChain = liveSyncInfo.actionsChain
640
+ .then(async () => {
505
641
  if (!liveSyncInfo.isStopped) {
506
642
  liveSyncInfo.currentSyncAction = action();
507
643
  const res = await liveSyncInfo.currentSyncAction;
508
644
  return res;
509
645
  }
646
+ })
647
+ .catch((err) => {
648
+ this.$logger.warn(`Error in action chain: ${err.message || err}`);
510
649
  });
511
650
  const result = await liveSyncInfo.actionsChain;
512
651
  return result;
@@ -33,6 +33,10 @@ class PrepareData extends controller_data_base_1.ControllerDataBase {
33
33
  this.watchNative = data.watchNative;
34
34
  }
35
35
  this.hostProjectPath = data.hostProjectPath;
36
+ if (data.skipNative) {
37
+ this.nativePrepare = { skipNativePrepare: true };
38
+ this.watchNative = false;
39
+ }
36
40
  this.uniqueBundle = !this.watch && data.uniqueBundle ? Date.now() : 0;
37
41
  }
38
42
  }
@@ -31,7 +31,7 @@ interface INodePackageManager {
31
31
  install(
32
32
  packageName: string,
33
33
  pathToSave: string,
34
- config: INodePackageManagerInstallOptions
34
+ config: INodePackageManagerInstallOptions,
35
35
  ): Promise<INpmInstallResultInfo>;
36
36
 
37
37
  /**
@@ -44,7 +44,7 @@ interface INodePackageManager {
44
44
  uninstall(
45
45
  packageName: string,
46
46
  config?: IDictionary<string | boolean>,
47
- path?: string
47
+ path?: string,
48
48
  ): Promise<string>;
49
49
 
50
50
  /**
@@ -84,7 +84,7 @@ interface INodePackageManager {
84
84
  */
85
85
  search(
86
86
  filter: string[],
87
- config: IDictionary<string | boolean>
87
+ config: IDictionary<string | boolean>,
88
88
  ): Promise<string>;
89
89
 
90
90
  /**
@@ -130,7 +130,7 @@ interface IPerformanceService {
130
130
  methodInfo: string,
131
131
  startTime: number,
132
132
  endTime: number,
133
- args: any[]
133
+ args: any[],
134
134
  ): void;
135
135
 
136
136
  // Will return a reference time in milliseconds
@@ -141,39 +141,39 @@ interface IPackageInstallationManager {
141
141
  install(
142
142
  packageName: string,
143
143
  packageDir: string,
144
- options?: INpmInstallOptions
144
+ options?: INpmInstallOptions,
145
145
  ): Promise<any>;
146
146
  uninstall(
147
147
  packageName: string,
148
148
  packageDir: string,
149
- options?: IDictionary<string | boolean>
149
+ options?: IDictionary<string | boolean>,
150
150
  ): Promise<any>;
151
151
  getLatestVersion(packageName: string): Promise<string>;
152
152
  getNextVersion(packageName: string): Promise<string>;
153
153
  getLatestCompatibleVersion(
154
154
  packageName: string,
155
- referenceVersion?: string
155
+ referenceVersion?: string,
156
156
  ): Promise<string>;
157
157
  getMaxSatisfyingVersion(
158
158
  packageName: string,
159
- versionRange: string
159
+ versionRange: string,
160
160
  ): Promise<string>;
161
161
  getLatestCompatibleVersionSafe(
162
162
  packageName: string,
163
- referenceVersion?: string
163
+ referenceVersion?: string,
164
164
  ): Promise<string>;
165
165
  getInspectorFromCache(
166
166
  inspectorNpmPackageName: string,
167
- projectDir: string
167
+ projectDir: string,
168
168
  ): Promise<string>;
169
169
  clearInspectorCache(): void;
170
170
  getInstalledDependencyVersion(
171
171
  packageName: string,
172
- projectDir?: string
172
+ projectDir?: string,
173
173
  ): Promise<string>;
174
174
  getMaxSatisfyingVersionSafe(
175
175
  packageName: string,
176
- versionIdentifier: string
176
+ versionIdentifier: string,
177
177
  ): Promise<string>;
178
178
  }
179
179
 
@@ -181,8 +181,7 @@ interface IPackageInstallationManager {
181
181
  * Describes options that can be passed to manipulate package installation.
182
182
  */
183
183
  interface INodePackageManagerInstallOptions
184
- extends INpmInstallConfigurationOptions,
185
- IDictionary<string | boolean> {
184
+ extends INpmInstallConfigurationOptions, IDictionary<string | boolean> {
186
185
  /**
187
186
  * Destination of the installation.
188
187
  * @type {string}
@@ -266,7 +265,7 @@ interface INpmPeerDependencyInfo {
266
265
  * @type {string}
267
266
  */
268
267
  requires: string;
269
- }
268
+ },
270
269
  ];
271
270
  /**
272
271
  * Dependencies of the dependency.
@@ -550,8 +549,7 @@ interface INpmInstallConfigurationOptionsBase {
550
549
  ignoreScripts: boolean; //npm flag
551
550
  }
552
551
 
553
- interface INpmInstallConfigurationOptions
554
- extends INpmInstallConfigurationOptionsBase {
552
+ interface INpmInstallConfigurationOptions extends INpmInstallConfigurationOptionsBase {
555
553
  disableNpmInstall: boolean;
556
554
  }
557
555
 
@@ -597,7 +595,8 @@ interface ITypingsOptions {
597
595
  }
598
596
 
599
597
  interface IOptions
600
- extends IRelease,
598
+ extends
599
+ IRelease,
601
600
  IDeviceIdentifier,
602
601
  IJustLaunch,
603
602
  IAvd,
@@ -622,7 +621,7 @@ interface IOptions
622
621
  argv: IYargArgv;
623
622
  validateOptions(
624
623
  commandSpecificDashedOptions?: IDictionary<IDashedOption>,
625
- projectData?: IProjectData
624
+ projectData?: IProjectData,
626
625
  ): void;
627
626
  options: IDictionary<IDashedOption>;
628
627
  shorthands: string[];
@@ -709,6 +708,7 @@ interface IOptions
709
708
  dryRun: boolean;
710
709
 
711
710
  platformOverride: string;
711
+ skipNative: boolean;
712
712
  uniqueBundle: boolean;
713
713
  // allow arbitrary options
714
714
  [optionName: string]: any;
@@ -719,26 +719,22 @@ interface IEnvOptions {
719
719
  }
720
720
 
721
721
  interface IAndroidBuildOptionsSettings
722
- extends IAndroidReleaseOptions,
723
- IRelease,
724
- Partial<IHasAndroidBundle> {}
722
+ extends IAndroidReleaseOptions, IRelease, Partial<IHasAndroidBundle> {}
725
723
 
726
724
  interface IHasAndroidBundle {
727
725
  androidBundle: boolean;
728
726
  }
729
727
 
730
728
  interface IPlatformBuildData
731
- extends IRelease,
732
- IHasUseHotModuleReloadOption,
733
- IBuildConfig,
734
- IEnvOptions {}
729
+ extends IRelease, IHasUseHotModuleReloadOption, IBuildConfig, IEnvOptions {}
735
730
 
736
731
  interface IDeviceEmulator extends IHasEmulatorOption, IDeviceIdentifier {}
737
732
 
738
733
  interface IRunPlatformOptions extends IJustLaunch, IDeviceEmulator {}
739
734
 
740
735
  interface IDeployPlatformOptions
741
- extends IAndroidReleaseOptions,
736
+ extends
737
+ IAndroidReleaseOptions,
742
738
  IRelease,
743
739
  IClean,
744
740
  IDeviceEmulator,
@@ -834,7 +830,7 @@ interface IAndroidToolsInfo {
834
830
  */
835
831
  validateJavacVersion(
836
832
  installedJavaVersion: string,
837
- options?: IAndroidToolsInfoOptions
833
+ options?: IAndroidToolsInfoOptions,
838
834
  ): boolean;
839
835
 
840
836
  /**
@@ -913,14 +909,14 @@ interface IAppDebugSocketProxyFactory extends NodeJS.EventEmitter {
913
909
  device: Mobile.IiOSDevice,
914
910
  appId: string,
915
911
  projectName: string,
916
- projectDir: string
912
+ projectDir: string,
917
913
  ): Promise<any>;
918
914
 
919
915
  ensureWebSocketProxy(
920
916
  device: Mobile.IiOSDevice,
921
917
  appId: string,
922
918
  projectName: string,
923
- projectDir: string
919
+ projectDir: string,
924
920
  ): Promise<any>;
925
921
 
926
922
  removeAllProxies(): void;
@@ -939,12 +935,12 @@ interface IiOSSocketRequestExecutor {
939
935
  executeAttachRequest(
940
936
  device: Mobile.IiOSDevice,
941
937
  timeout: number,
942
- projectId: string
938
+ projectId: string,
943
939
  ): Promise<void>;
944
940
  executeRefreshRequest(
945
941
  device: Mobile.IiOSDevice,
946
942
  timeout: number,
947
- appId: string
943
+ appId: string,
948
944
  ): Promise<boolean>;
949
945
  }
950
946
 
@@ -995,7 +991,7 @@ interface IProjectNameService {
995
991
  */
996
992
  ensureValidName(
997
993
  projectName: string,
998
- validateOptions?: { force: boolean }
994
+ validateOptions?: { force: boolean },
999
995
  ): Promise<string>;
1000
996
  }
1001
997
 
@@ -1089,7 +1085,7 @@ interface IBundleValidatorHelper {
1089
1085
  */
1090
1086
  getBundlerDependencyVersion(
1091
1087
  projectData: IProjectData,
1092
- bundlerName?: string
1088
+ bundlerName?: string,
1093
1089
  ): string;
1094
1090
  }
1095
1091
 
@@ -1171,7 +1167,7 @@ interface IAssetsGenerationService {
1171
1167
  * @returns {Promise<void>}
1172
1168
  */
1173
1169
  generateSplashScreens(
1174
- splashesGenerationData: IResourceGenerationData
1170
+ splashesGenerationData: IResourceGenerationData,
1175
1171
  ): Promise<void>;
1176
1172
  }
1177
1173
 
@@ -1207,7 +1203,7 @@ interface IPlatformValidationService {
1207
1203
  provision: true | string,
1208
1204
  teamId: true | string,
1209
1205
  projectData: IProjectData,
1210
- platform?: string
1206
+ platform?: string,
1211
1207
  ): Promise<boolean>;
1212
1208
 
1213
1209
  validatePlatformInstalled(platform: string, projectData: IProjectData): void;
@@ -1220,7 +1216,7 @@ interface IPlatformValidationService {
1220
1216
  */
1221
1217
  isPlatformSupportedForOS(
1222
1218
  platform: string,
1223
- projectData: IProjectData
1219
+ projectData: IProjectData,
1224
1220
  ): boolean;
1225
1221
  }
1226
1222
 
@@ -1228,27 +1224,27 @@ interface IPlatformCommandHelper {
1228
1224
  addPlatforms(
1229
1225
  platforms: string[],
1230
1226
  projectData: IProjectData,
1231
- frameworkPath?: string
1227
+ frameworkPath?: string,
1232
1228
  ): Promise<void>;
1233
1229
  cleanPlatforms(
1234
1230
  platforms: string[],
1235
1231
  projectData: IProjectData,
1236
- frameworkPath: string
1232
+ frameworkPath: string,
1237
1233
  ): Promise<void>;
1238
1234
  removePlatforms(
1239
1235
  platforms: string[],
1240
- projectData: IProjectData
1236
+ projectData: IProjectData,
1241
1237
  ): Promise<void>;
1242
1238
  updatePlatforms(
1243
1239
  platforms: string[],
1244
- projectData: IProjectData
1240
+ projectData: IProjectData,
1245
1241
  ): Promise<void>;
1246
1242
  getInstalledPlatforms(projectData: IProjectData): string[];
1247
1243
  getAvailablePlatforms(projectData: IProjectData): string[];
1248
1244
  getPreparedPlatforms(projectData: IProjectData): string[];
1249
1245
  getCurrentPlatformVersion(
1250
1246
  platform: string,
1251
- projectData: IProjectData
1247
+ projectData: IProjectData,
1252
1248
  ): string;
1253
1249
  }
1254
1250
 
package/lib/options.js CHANGED
@@ -225,6 +225,7 @@ class Options {
225
225
  default: true,
226
226
  },
227
227
  dryRun: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
228
+ skipNative: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
228
229
  uniqueBundle: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
229
230
  };
230
231
  }
@@ -265,8 +265,7 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
265
265
  return Promise.resolve();
266
266
  }
267
267
  async isDynamicFramework(frameworkPath) {
268
- const isDynamicFrameworkBundle = async (bundlePath, frameworkName) => {
269
- const frameworkBinaryPath = path.join(bundlePath, frameworkName);
268
+ const isDynamicFrameworkBundle = async (frameworkBinaryPath) => {
270
269
  const fileResult = (await this.$childProcess.spawnFromEvent("file", [frameworkBinaryPath], "close")).stdout;
271
270
  const isDynamicallyLinked = _.includes(fileResult, "dynamically linked");
272
271
  return isDynamicallyLinked;
@@ -278,7 +277,11 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
278
277
  const singlePlatformFramework = path.join(frameworkPath, library.LibraryIdentifier, library.LibraryPath);
279
278
  if (this.$fs.exists(singlePlatformFramework)) {
280
279
  const frameworkName = path.basename(singlePlatformFramework, path.extname(singlePlatformFramework));
281
- isDynamic = await isDynamicFrameworkBundle(singlePlatformFramework, frameworkName);
280
+ let frameworkBinaryPath = path.join(singlePlatformFramework, frameworkName);
281
+ if (library.BinaryPath) {
282
+ frameworkBinaryPath = path.join(frameworkPath, library.LibraryIdentifier, library.BinaryPath);
283
+ }
284
+ isDynamic = await isDynamicFrameworkBundle(frameworkBinaryPath);
282
285
  break;
283
286
  }
284
287
  }
@@ -286,7 +289,7 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
286
289
  }
287
290
  else {
288
291
  const frameworkName = path.basename(frameworkPath, path.extname(frameworkPath));
289
- return await isDynamicFrameworkBundle(frameworkPath, frameworkName);
292
+ return await isDynamicFrameworkBundle(path.join(frameworkPath, frameworkName));
290
293
  }
291
294
  }
292
295
  /**
@@ -623,6 +626,27 @@ class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase
623
626
  await this.prepareFrameworks(pluginPlatformsFolderPath, pluginData, projectData);
624
627
  await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData, projectData);
625
628
  }
629
+ shouldRepreparePlugin(pluginData, projectData) {
630
+ const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
631
+ for (const fileName of this.getAllLibsForPluginWithFileExtension(pluginData, ".a")) {
632
+ const staticLibPath = path.join(pluginPlatformsFolderPath, fileName);
633
+ const libraryName = path.basename(staticLibPath, ".a");
634
+ const headersSubpath = path.join(path.dirname(staticLibPath), "include", libraryName);
635
+ if (!this.$fs.exists(headersSubpath)) {
636
+ continue;
637
+ }
638
+ const headerFiles = this.$fs
639
+ .readDirectory(headersSubpath)
640
+ .filter((f) => path.extname(f) === ".h" &&
641
+ this.$fs.getFsStats(path.join(headersSubpath, f)).isFile());
642
+ if (headerFiles.length > 0 &&
643
+ !this.$fs.exists(path.join(headersSubpath, "module.modulemap"))) {
644
+ this.$logger.trace(`Plugin ${pluginData.name}: modulemap missing at ${headersSubpath}, will re-prepare`);
645
+ return true;
646
+ }
647
+ }
648
+ return false;
649
+ }
626
650
  async removePluginNativeCode(pluginData, projectData) {
627
651
  const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME);
628
652
  this.removeNativeSourceCode(pluginPlatformsFolderPath, pluginData, projectData);
@@ -107,6 +107,7 @@ class PluginsService {
107
107
  }
108
108
  }
109
109
  async preparePluginNativeCode({ pluginData, platform, projectData, }) {
110
+ var _a, _b, _c;
110
111
  const platformData = this.$platformsDataService.getPlatformData(platform, projectData);
111
112
  const pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(platform);
112
113
  if (this.$fs.exists(pluginPlatformsFolderPath)) {
@@ -114,8 +115,10 @@ class PluginsService {
114
115
  const allPluginsNativeHashes = this.getAllPluginsNativeHashes(pathToPluginsBuildFile);
115
116
  const oldPluginNativeHashes = allPluginsNativeHashes[pluginData.name];
116
117
  const currentPluginNativeHashes = await this.getPluginNativeHashes(pluginPlatformsFolderPath);
117
- if (!oldPluginNativeHashes ||
118
- this.$filesHashService.hasChangesInShasums(oldPluginNativeHashes, currentPluginNativeHashes)) {
118
+ const needsReprepare = !oldPluginNativeHashes ||
119
+ this.$filesHashService.hasChangesInShasums(oldPluginNativeHashes, currentPluginNativeHashes) ||
120
+ ((_c = (_b = (_a = platformData.platformProjectService).shouldRepreparePlugin) === null || _b === void 0 ? void 0 : _b.call(_a, pluginData, projectData)) !== null && _c !== void 0 ? _c : false);
121
+ if (needsReprepare) {
119
122
  await platformData.platformProjectService.preparePluginNativeCode(pluginData, projectData);
120
123
  const updatedPluginNativeHashes = await this.getPluginNativeHashes(pluginPlatformsFolderPath);
121
124
  this.setPluginNativeHashes({
@@ -366,7 +369,12 @@ This framework comes from ${dependencyName} plugin, which is installed multiple
366
369
  getNodeModuleData(module, projectDir) {
367
370
  // module can be modulePath or moduleName
368
371
  if (!this.$fs.exists(module) || path.basename(module) !== "package.json") {
369
- module = this.getPackageJsonFilePathForModule(module, projectDir);
372
+ const resolvedPath = this.getPackageJsonFilePathForModule(module, projectDir);
373
+ if (!resolvedPath) {
374
+ this.$logger.warn(`Could not find module ${color_1.color.yellow(module)}. It may have been removed or is not installed. Skipping.`);
375
+ return null;
376
+ }
377
+ module = resolvedPath;
370
378
  }
371
379
  const data = this.$fs.readJson(module);
372
380
  return {
@@ -384,7 +392,7 @@ This framework comes from ${dependencyName} plugin, which is installed multiple
384
392
  async getAllInstalledModules(projectData) {
385
393
  await this.ensure(projectData);
386
394
  const nodeModules = this.getDependencies(projectData.projectDir);
387
- return _.map(nodeModules, (nodeModuleName) => this.getNodeModuleData(nodeModuleName, projectData.projectDir));
395
+ return _.map(nodeModules, (nodeModuleName) => this.getNodeModuleData(nodeModuleName, projectData.projectDir)).filter(Boolean);
388
396
  }
389
397
  async executeNpmCommand(npmCommandName, npmCommandArguments, projectData) {
390
398
  if (npmCommandName === PluginsService.INSTALL_COMMAND_NAME) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nativescript",
3
3
  "main": "./lib/nativescript-cli-lib.js",
4
- "version": "9.1.0-alpha.2",
4
+ "version": "9.1.0-alpha.3",
5
5
  "author": "NativeScript <oss@nativescript.org>",
6
6
  "description": "Command-line interface for building NativeScript projects",
7
7
  "bin": {