nativescript 9.1.0-alpha.2 → 9.1.0-alpha.4
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.
- package/lib/commands/prepare.js +5 -0
- package/lib/controllers/run-controller.js +200 -13
- package/lib/data/prepare-data.js +4 -0
- package/lib/declarations.d.ts +38 -42
- package/lib/options.js +1 -0
- package/lib/services/bundler/bundler-compiler-service.js +155 -0
- package/lib/services/ios-project-service.js +28 -4
- package/lib/services/plugins-service.js +12 -4
- package/package.json +1 -1
package/lib/commands/prepare.js
CHANGED
|
@@ -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
|
-
|
|
66
|
+
if (!changesInfo.hasChanges) {
|
|
67
|
+
return;
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
|
-
|
|
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, "pre-build");
|
|
280
289
|
const prepareResultData = await this.$prepareController.prepare(prepareData);
|
|
281
290
|
const buildData = {
|
|
282
291
|
...deviceDescriptor.buildData,
|
|
@@ -327,6 +336,11 @@ class RunController extends events_1.EventEmitter {
|
|
|
327
336
|
watch: !skipWatcher,
|
|
328
337
|
liveSyncDeviceData: deviceDescriptor,
|
|
329
338
|
});
|
|
339
|
+
// Re-establish the adb reverse on the CURRENT transport right
|
|
340
|
+
// before launch — the transport can change during build/install
|
|
341
|
+
// and drop the mapping set in `pre-build`, which would leave the
|
|
342
|
+
// app unable to reach the Vite dev server at 127.0.0.1.
|
|
343
|
+
await this.setupAndroidViteHmrReverse(device, projectData, liveSyncInfo, "pre-launch");
|
|
330
344
|
await this.refreshApplication(projectData, liveSyncResultInfo, null, deviceDescriptor);
|
|
331
345
|
this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`);
|
|
332
346
|
this.emitCore(constants_2.RunOnDeviceEvents.runOnDeviceStarted, {
|
|
@@ -353,6 +367,134 @@ class RunController extends events_1.EventEmitter {
|
|
|
353
367
|
};
|
|
354
368
|
await this.addActionToChain(projectData.projectDir, () => this.$devicesService.execute(deviceAction, (device) => _.some(deviceDescriptors, (deviceDescriptor) => deviceDescriptor.identifier === device.deviceInfo.identifier)));
|
|
355
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Set up `adb reverse tcp:<port> tcp:<port>` for an Android device
|
|
372
|
+
* when the project bundles with Vite in HMR/watch mode, then export
|
|
373
|
+
* the result to the bundler subprocess via environment variables.
|
|
374
|
+
*
|
|
375
|
+
* The Vite dev-host helper prefers an ADB tunnel (device-side
|
|
376
|
+
* `127.0.0.1:<port>` → host) over the emulator's flaky slirp NAT
|
|
377
|
+
* (`10.0.2.2`). Historically the bundler tried to wire that tunnel
|
|
378
|
+
* itself at config-load time, racing this CLI's device discovery
|
|
379
|
+
* over the single global adb daemon and intermittently freezing the
|
|
380
|
+
* run at "Searching for devices…". The CLI is the right owner: it
|
|
381
|
+
* knows the exact target serial and when the device is ready, and it
|
|
382
|
+
* already drives a single, version-matched adb. We do the reverse
|
|
383
|
+
* here and signal the bundler with `NS_ADB_REVERSE_READY=1` so it
|
|
384
|
+
* never spawns adb on its own.
|
|
385
|
+
*
|
|
386
|
+
* Best-effort: any failure is logged at trace level and swallowed.
|
|
387
|
+
* The bundler then falls back to its own (now hardened) adb path, or
|
|
388
|
+
* ultimately to `10.0.2.2`, so a reverse hiccup never fails the run.
|
|
389
|
+
*/
|
|
390
|
+
async setupAndroidViteHmrReverse(device, projectData, liveSyncInfo, phase) {
|
|
391
|
+
try {
|
|
392
|
+
if (!this.$mobileHelper.isAndroidPlatform(device.deviceInfo.platform)) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (projectData.bundler !== "vite") {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
// HMR over the tunnel only matters for a live watch session.
|
|
399
|
+
if (liveSyncInfo.skipWatcher || !liveSyncInfo.useHotModuleReload) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
// Respect the user's explicit opt-out — they want the
|
|
403
|
+
// `10.0.2.2` / LAN path, so don't create a tunnel or claim one
|
|
404
|
+
// exists.
|
|
405
|
+
if (this.isTruthyEnvFlag(process.env.NS_HMR_NO_ADB_REVERSE)) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
// `NS_HMR_PREFER_LAN_HOST` means the dev wants LAN routing
|
|
409
|
+
// (physical device over Wi-Fi); the dev-host resolver suppresses
|
|
410
|
+
// the adb-reverse path for it, so don't bother wiring one.
|
|
411
|
+
if (this.isTruthyEnvFlag(process.env.NS_HMR_PREFER_LAN_HOST)) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const serial = device.deviceInfo.identifier;
|
|
415
|
+
const port = this.getViteHmrPort();
|
|
416
|
+
if (phase === "pre-build") {
|
|
417
|
+
// Decide the origin baked into bundle.mjs. Hand the bundler our
|
|
418
|
+
// exact adb (so any self-managed fallback can't version-mismatch
|
|
419
|
+
// the daemon) and, if the tunnel comes up, tell it to emit
|
|
420
|
+
// `127.0.0.1` and skip adb entirely.
|
|
421
|
+
process.env.NS_ADB_PATH = await this.$staticConfig.getAdbFilePath();
|
|
422
|
+
process.env.NS_DEVICE_SERIAL = serial;
|
|
423
|
+
const ok = await this.ensureAndroidReverse(device, serial, port);
|
|
424
|
+
if (ok) {
|
|
425
|
+
process.env.NS_ADB_REVERSE_READY = "1";
|
|
426
|
+
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).`);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
this.$logger.warn(`Could not confirm 'adb reverse tcp:${port}' on ${serial} (device adbd slow/unresponsive). Vite HMR will fall back to 10.0.2.2. If this persists, cold-boot/wipe the emulator, or set NS_HMR_NO_ADB_REVERSE=1.`);
|
|
430
|
+
}
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
// phase === "pre-launch": re-establish the mapping right before the
|
|
434
|
+
// app boots. `adb reverse` mappings are bound to the device's adb
|
|
435
|
+
// transport, and that transport can change during the (long) build
|
|
436
|
+
// + install (fresh emulators reconnect as they settle), silently
|
|
437
|
+
// dropping the early mapping. We only bother when we actually told
|
|
438
|
+
// the bundle to use `127.0.0.1` (READY set during pre-build).
|
|
439
|
+
if (!this.isTruthyEnvFlag(process.env.NS_ADB_REVERSE_READY)) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const ok = await this.ensureAndroidReverse(device, serial, port);
|
|
443
|
+
if (!ok) {
|
|
444
|
+
this.$logger.warn(`adb reverse tcp:${port} was not active before launch on ${serial}; the app may fail to reach the Vite dev server at 127.0.0.1:${port}.`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
this.$logger.trace(`Setting up adb reverse for Vite HMR (${phase}) failed; leaving it to the bundler fallback. Error: ${err}`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Apply `adb reverse tcp:<port> tcp:<port>` to the device and confirm
|
|
453
|
+
* via `adb reverse --list` that it actually landed, retrying a few
|
|
454
|
+
* times. Every device-side call is bounded with a Node `spawn` timeout
|
|
455
|
+
* + `SIGKILL` so a wedged/slow adbd (observed blocking 90s+ on some
|
|
456
|
+
* fresh-boot / API-36 arm64 emulators) can never hang the run — the
|
|
457
|
+
* hung adb child is reaped, not orphaned. Returns whether the mapping
|
|
458
|
+
* is confirmed present.
|
|
459
|
+
*/
|
|
460
|
+
async ensureAndroidReverse(device, serial, port) {
|
|
461
|
+
var _a, _b, _c;
|
|
462
|
+
const adb = device.adb;
|
|
463
|
+
const ADB_WAIT_MS = 15000;
|
|
464
|
+
const ADB_REVERSE_MS = 20000;
|
|
465
|
+
const bounded = (timeout) => ({
|
|
466
|
+
deviceIdentifier: serial,
|
|
467
|
+
treatErrorsAsWarnings: true,
|
|
468
|
+
childProcessOptions: { timeout, killSignal: "SIGKILL" },
|
|
469
|
+
});
|
|
470
|
+
// `wait-for-device` only blocks until the transport is up; bounded so a
|
|
471
|
+
// never-ready device can't stall us.
|
|
472
|
+
await adb.executeCommand(["wait-for-device"], bounded(ADB_WAIT_MS));
|
|
473
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
474
|
+
await adb.executeCommand(["reverse", `tcp:${port}`, `tcp:${port}`], bounded(ADB_REVERSE_MS));
|
|
475
|
+
// Verify it landed (a SIGKILL'd-on-timeout reverse resolves rather
|
|
476
|
+
// than throws, so success of the call isn't proof).
|
|
477
|
+
const list = (_c = (_b = (_a = (await adb.executeCommand(["reverse", "--list"], bounded(ADB_WAIT_MS)))) === null || _a === void 0 ? void 0 : _a.toString) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : "";
|
|
478
|
+
if (list.includes(`tcp:${port}`)) {
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
getViteHmrPort() {
|
|
485
|
+
// The Vite dev server defaults to 5173; the bundler reads the same
|
|
486
|
+
// default. If a project runs Vite on a different port, the dev sets
|
|
487
|
+
// `NS_HMR_PORT` so the CLI reverses the matching port.
|
|
488
|
+
const fromEnv = Number(process.env.NS_HMR_PORT);
|
|
489
|
+
return Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : 5173;
|
|
490
|
+
}
|
|
491
|
+
isTruthyEnvFlag(value) {
|
|
492
|
+
if (typeof value !== "string") {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
const v = value.trim().toLowerCase();
|
|
496
|
+
return !!v && v !== "0" && v !== "false" && v !== "off" && v !== "no";
|
|
497
|
+
}
|
|
356
498
|
async syncChangedDataOnDevices(data, projectData, liveSyncInfo) {
|
|
357
499
|
const successfullySyncedMessageFormat = `Successfully synced application %s on device %s.`;
|
|
358
500
|
const rebuiltInformation = {};
|
|
@@ -476,18 +618,13 @@ class RunController extends events_1.EventEmitter {
|
|
|
476
618
|
}
|
|
477
619
|
}
|
|
478
620
|
catch (err) {
|
|
479
|
-
this.$logger.warn(`Unable to apply changes for device: ${device.deviceInfo.identifier}. Error is: ${err && err.message}.`);
|
|
621
|
+
this.$logger.warn(`Unable to apply changes for device: ${device.deviceInfo.identifier}. Error is: ${err && err.message}. Will retry on next change.`);
|
|
480
622
|
this.emitCore(constants_2.RunOnDeviceEvents.runOnDeviceError, {
|
|
481
623
|
projectDir: projectData.projectDir,
|
|
482
624
|
deviceIdentifier: device.deviceInfo.identifier,
|
|
483
625
|
applicationIdentifier: projectData.projectIdentifiers[device.deviceInfo.platform.toLowerCase()],
|
|
484
626
|
error: err,
|
|
485
627
|
});
|
|
486
|
-
await this.stop({
|
|
487
|
-
projectDir: projectData.projectDir,
|
|
488
|
-
deviceIdentifiers: [device.deviceInfo.identifier],
|
|
489
|
-
stopOptions: { shouldAwaitAllActions: false },
|
|
490
|
-
});
|
|
491
628
|
}
|
|
492
629
|
};
|
|
493
630
|
await this.addActionToChain(projectData.projectDir, () => this.$devicesService.execute(deviceAction, (device) => {
|
|
@@ -498,15 +635,65 @@ class RunController extends events_1.EventEmitter {
|
|
|
498
635
|
_.some(liveSyncProcessInfo.deviceDescriptors, (deviceDescriptor) => deviceDescriptor.identifier === device.deviceInfo.identifier));
|
|
499
636
|
}));
|
|
500
637
|
}
|
|
638
|
+
scheduleSyncOnDevices(data, projectData, liveSyncInfo) {
|
|
639
|
+
if (this._syncInProgress) {
|
|
640
|
+
const platform = data.platform;
|
|
641
|
+
const existing = this._pendingSyncs.get(platform);
|
|
642
|
+
if (existing) {
|
|
643
|
+
existing.data = this.mergeFilesChangeEvents(existing.data, data);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
this._pendingSyncs.set(platform, { data, projectData, liveSyncInfo });
|
|
647
|
+
}
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
this.executeSyncOnDevices(data, projectData, liveSyncInfo);
|
|
651
|
+
}
|
|
652
|
+
async executeSyncOnDevices(data, projectData, liveSyncInfo) {
|
|
653
|
+
this._syncInProgress = true;
|
|
654
|
+
try {
|
|
655
|
+
await this.syncChangedDataOnDevices(data, projectData, liveSyncInfo);
|
|
656
|
+
}
|
|
657
|
+
catch (err) {
|
|
658
|
+
this.$logger.trace(`Error during sync on devices: ${err.message || err}`);
|
|
659
|
+
}
|
|
660
|
+
finally {
|
|
661
|
+
const nextEntry = this._pendingSyncs.entries().next();
|
|
662
|
+
if (!nextEntry.done) {
|
|
663
|
+
const [platform, pending] = nextEntry.value;
|
|
664
|
+
this._pendingSyncs.delete(platform);
|
|
665
|
+
this.executeSyncOnDevices(pending.data, pending.projectData, pending.liveSyncInfo);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
this._syncInProgress = false;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
mergeFilesChangeEvents(a, b) {
|
|
673
|
+
return {
|
|
674
|
+
files: [...new Set([...a.files, ...b.files])],
|
|
675
|
+
staleFiles: [
|
|
676
|
+
...new Set([...(a.staleFiles || []), ...(b.staleFiles || [])]),
|
|
677
|
+
],
|
|
678
|
+
hasOnlyHotUpdateFiles: a.hasOnlyHotUpdateFiles && b.hasOnlyHotUpdateFiles,
|
|
679
|
+
hasNativeChanges: a.hasNativeChanges || b.hasNativeChanges,
|
|
680
|
+
hmrData: b.hmrData,
|
|
681
|
+
platform: b.platform,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
501
684
|
async addActionToChain(projectDir, action) {
|
|
502
685
|
const liveSyncInfo = this.$liveSyncProcessDataService.getPersistedData(projectDir);
|
|
503
686
|
if (liveSyncInfo) {
|
|
504
|
-
liveSyncInfo.actionsChain = liveSyncInfo.actionsChain
|
|
687
|
+
liveSyncInfo.actionsChain = liveSyncInfo.actionsChain
|
|
688
|
+
.then(async () => {
|
|
505
689
|
if (!liveSyncInfo.isStopped) {
|
|
506
690
|
liveSyncInfo.currentSyncAction = action();
|
|
507
691
|
const res = await liveSyncInfo.currentSyncAction;
|
|
508
692
|
return res;
|
|
509
693
|
}
|
|
694
|
+
})
|
|
695
|
+
.catch((err) => {
|
|
696
|
+
this.$logger.warn(`Error in action chain: ${err.message || err}`);
|
|
510
697
|
});
|
|
511
698
|
const result = await liveSyncInfo.actionsChain;
|
|
512
699
|
return result;
|
package/lib/data/prepare-data.js
CHANGED
|
@@ -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
|
}
|
package/lib/declarations.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
}
|
|
@@ -8,6 +8,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.BundlerCompilerService = void 0;
|
|
10
10
|
const path = require("path");
|
|
11
|
+
const net = require("net");
|
|
11
12
|
const semver = require("semver");
|
|
12
13
|
const _ = require("lodash");
|
|
13
14
|
const events_1 = require("events");
|
|
@@ -34,6 +35,10 @@ class BundlerCompilerService extends events_1.EventEmitter {
|
|
|
34
35
|
this.$packageInstallationManager = $packageInstallationManager;
|
|
35
36
|
this.$projectConfigService = $projectConfigService;
|
|
36
37
|
this.bundlerProcesses = {};
|
|
38
|
+
// Vite-only: the long-lived `vite serve` dev server the device fetches
|
|
39
|
+
// modules and HMR updates from. Keyed by platform, managed by this CLI
|
|
40
|
+
// so users no longer need a separate `concurrently`/`wait-on` process.
|
|
41
|
+
this.viteServeProcesses = {};
|
|
37
42
|
this.expectedHashes = {};
|
|
38
43
|
}
|
|
39
44
|
getViteDistOutputPath(projectDir) {
|
|
@@ -47,6 +52,14 @@ class BundlerCompilerService extends events_1.EventEmitter {
|
|
|
47
52
|
}
|
|
48
53
|
let isFirstBundlerWatchCompilation = true;
|
|
49
54
|
prepareData.watch = true;
|
|
55
|
+
// Bring up the Vite HMR dev server the device fetches modules /
|
|
56
|
+
// HMR updates from. No-op unless bundler is vite + hmr + watch.
|
|
57
|
+
// Fired in parallel with the build watcher; both child processes
|
|
58
|
+
// inherit the adb-reverse env the run-controller set before
|
|
59
|
+
// prepare, so neither one spawns adb on its own. Intentionally not
|
|
60
|
+
// awaited — the device only connects to it at app launch, well
|
|
61
|
+
// after the first build.
|
|
62
|
+
this.startViteDevServer(platformData, projectData, prepareData);
|
|
50
63
|
try {
|
|
51
64
|
const childProcess = await this.startBundleProcess(platformData, projectData, prepareData);
|
|
52
65
|
// Handle Vite differently from webpack
|
|
@@ -346,6 +359,141 @@ class BundlerCompilerService extends events_1.EventEmitter {
|
|
|
346
359
|
await this.$cleanupService.addKillProcess(childProcess.pid.toString());
|
|
347
360
|
return childProcess;
|
|
348
361
|
}
|
|
362
|
+
getViteHmrPort() {
|
|
363
|
+
const fromEnv = Number(process.env.NS_HMR_PORT);
|
|
364
|
+
return Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : 5173;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Spawn and manage the Vite dev server (`vite serve`) for HMR.
|
|
368
|
+
*
|
|
369
|
+
* Why the CLI owns this. With Vite, HMR needs a long-lived dev server
|
|
370
|
+
* (HTTP + the `/ns-hmr` websocket on port 5173) that the device fetches
|
|
371
|
+
* modules and hot updates from — it is SEPARATE from the
|
|
372
|
+
* `vite build --watch` process that emits the `bundle.mjs` bootstrap
|
|
373
|
+
* baked into the app. Historically users wired this up themselves with
|
|
374
|
+
* `concurrently`/`wait-on`, which left two uncoordinated processes both
|
|
375
|
+
* touching adb during cold start (the source of the Android
|
|
376
|
+
* "Searching for devices…" freeze). By spawning it here as a child of
|
|
377
|
+
* the CLI, the dev server inherits the CLI's environment — crucially
|
|
378
|
+
* `NS_ADB_REVERSE_READY`/`NS_DEVICE_SERIAL`/`NS_ADB_PATH` set by the
|
|
379
|
+
* run-controller — so the CLI is the single adb owner and the dev
|
|
380
|
+
* server never spawns adb itself.
|
|
381
|
+
*
|
|
382
|
+
* No-op unless bundler is vite, HMR is on, watch mode, and not release.
|
|
383
|
+
* Best-effort: failures are logged, never thrown — a dev-server hiccup
|
|
384
|
+
* must not fail the run.
|
|
385
|
+
*/
|
|
386
|
+
async startViteDevServer(platformData, projectData, prepareData) {
|
|
387
|
+
try {
|
|
388
|
+
if (this.getBundler() !== "vite") {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (!prepareData.watch || !prepareData.hmr || prepareData.release) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const key = platformData.platformNameLowerCase;
|
|
395
|
+
if (this.viteServeProcesses[key]) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const port = this.getViteHmrPort();
|
|
399
|
+
// One dev server per port. Simultaneous multi-platform HMR in a
|
|
400
|
+
// single CLI invocation would collide on 5173 — that case still
|
|
401
|
+
// needs a distinct NS_HMR_PORT per platform, so skip + warn rather
|
|
402
|
+
// than fail to bind.
|
|
403
|
+
const collidingPlatform = Object.keys(this.viteServeProcesses)[0];
|
|
404
|
+
if (collidingPlatform) {
|
|
405
|
+
this.$logger.warn(`Vite dev server already running for '${collidingPlatform}' on port ${port}; skipping a second server for '${key}'. For simultaneous multi-platform HMR, set a distinct NS_HMR_PORT per platform.`);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const envData = this.buildEnvData(platformData.platformNameLowerCase, projectData, prepareData);
|
|
409
|
+
const cliArgs = await this.buildEnvCommandLineParams(envData, platformData, projectData, prepareData);
|
|
410
|
+
const additionalNodeArgs = semver.major(process.version) <= 8 ? ["--harmony"] : [];
|
|
411
|
+
if (await this.shouldUsePreserveSymlinksOption()) {
|
|
412
|
+
additionalNodeArgs.push("--preserve-symlinks");
|
|
413
|
+
}
|
|
414
|
+
// `vite serve` (not `build`): runs the dev server and watches on
|
|
415
|
+
// its own — no `--watch`. Env flags (`--env.android --env.hmr …`)
|
|
416
|
+
// go after `--` so vite's CLI doesn't choke on unknown options.
|
|
417
|
+
const args = [
|
|
418
|
+
...additionalNodeArgs,
|
|
419
|
+
this.getBundlerExecutablePath(projectData),
|
|
420
|
+
"serve",
|
|
421
|
+
`--config=${projectData.bundlerConfigPath}`,
|
|
422
|
+
`--mode=development`,
|
|
423
|
+
"--",
|
|
424
|
+
...cliArgs,
|
|
425
|
+
].filter(Boolean);
|
|
426
|
+
const options = {
|
|
427
|
+
cwd: projectData.projectDir,
|
|
428
|
+
// Inherit so the dev server's URLs/logs stream to the user as
|
|
429
|
+
// before. No IPC needed here — the build watcher provides the
|
|
430
|
+
// bundle-complete IPC; the dev server is fetched over HTTP/ws.
|
|
431
|
+
stdio: "inherit",
|
|
432
|
+
env: {
|
|
433
|
+
...process.env,
|
|
434
|
+
NATIVESCRIPT_BUNDLER_ENV: JSON.stringify(envData),
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
if (this.$hostInfo.isWindows) {
|
|
438
|
+
Object.assign(options.env, { APPDATA: process.env.appData });
|
|
439
|
+
}
|
|
440
|
+
this.$logger.info(`Starting Vite dev server (HMR) for ${key} on port ${port}…`);
|
|
441
|
+
const childProcess = this.$childProcess.spawn(process.execPath, args, options);
|
|
442
|
+
this.viteServeProcesses[key] = childProcess;
|
|
443
|
+
await this.$cleanupService.addKillProcess(childProcess.pid.toString());
|
|
444
|
+
childProcess.once("exit", (code) => {
|
|
445
|
+
delete this.viteServeProcesses[key];
|
|
446
|
+
if (code) {
|
|
447
|
+
this.$logger.warn(`Vite dev server for ${key} exited with code ${code}.`);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
// Bounded readiness probe so we surface a clear log once the
|
|
451
|
+
// device can actually reach modules (or warn if it never binds).
|
|
452
|
+
const ready = await this.waitForPort(port, 30000);
|
|
453
|
+
if (ready) {
|
|
454
|
+
this.$logger.info(`Vite dev server ready on port ${port} (HMR for ${key}).`);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
this.$logger.warn(`Vite dev server did not open port ${port} within 30s; HMR may be unavailable.`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
catch (err) {
|
|
461
|
+
this.$logger.warn(`Failed to start the Vite dev server: ${err}. HMR may be unavailable.`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Resolve true once `127.0.0.1:<port>` accepts a TCP connection, or
|
|
466
|
+
* false after `timeoutMs`. Used to detect the Vite dev server is up.
|
|
467
|
+
*/
|
|
468
|
+
waitForPort(port, timeoutMs) {
|
|
469
|
+
const deadline = Date.now() + timeoutMs;
|
|
470
|
+
return new Promise((resolve) => {
|
|
471
|
+
const attempt = () => {
|
|
472
|
+
const socket = net.connect({ port, host: "127.0.0.1" });
|
|
473
|
+
let settled = false;
|
|
474
|
+
const done = (ok) => {
|
|
475
|
+
if (settled) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
settled = true;
|
|
479
|
+
socket.destroy();
|
|
480
|
+
if (ok) {
|
|
481
|
+
resolve(true);
|
|
482
|
+
}
|
|
483
|
+
else if (Date.now() >= deadline) {
|
|
484
|
+
resolve(false);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
setTimeout(attempt, 250);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
socket.once("connect", () => done(true));
|
|
491
|
+
socket.once("error", () => done(false));
|
|
492
|
+
socket.setTimeout(1000, () => done(false));
|
|
493
|
+
};
|
|
494
|
+
attempt();
|
|
495
|
+
});
|
|
496
|
+
}
|
|
349
497
|
buildEnvData(platform, projectData, prepareData) {
|
|
350
498
|
var _a, _b, _c;
|
|
351
499
|
const { env } = prepareData;
|
|
@@ -477,6 +625,13 @@ class BundlerCompilerService extends events_1.EventEmitter {
|
|
|
477
625
|
bundlerProcess.kill("SIGINT");
|
|
478
626
|
delete this.bundlerProcesses[platform];
|
|
479
627
|
}
|
|
628
|
+
// Tear down the Vite dev server we manage alongside the build watcher.
|
|
629
|
+
const viteServeProcess = this.viteServeProcesses[platform];
|
|
630
|
+
if (viteServeProcess) {
|
|
631
|
+
await this.$cleanupService.removeKillProcess(viteServeProcess.pid.toString());
|
|
632
|
+
viteServeProcess.kill("SIGINT");
|
|
633
|
+
delete this.viteServeProcesses[platform];
|
|
634
|
+
}
|
|
480
635
|
}
|
|
481
636
|
handleHMRMessage(message, platformData, projectData, prepareData) {
|
|
482
637
|
// handle new bundler hmr packets
|
|
@@ -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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
4
|
+
"version": "9.1.0-alpha.4",
|
|
5
5
|
"author": "NativeScript <oss@nativescript.org>",
|
|
6
6
|
"description": "Command-line interface for building NativeScript projects",
|
|
7
7
|
"bin": {
|