nativescript 9.1.0-alpha.3 → 9.1.0-alpha.5
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.
|
@@ -285,7 +285,7 @@ class RunController extends events_1.EventEmitter {
|
|
|
285
285
|
// spawns the Vite bundler that inherits `process.env`) so the
|
|
286
286
|
// bundler trusts the tunnel instead of racing us to spawn its
|
|
287
287
|
// own adb during config-load. See packages/vite hardening.
|
|
288
|
-
await this.setupAndroidViteHmrReverse(device, projectData, liveSyncInfo);
|
|
288
|
+
await this.setupAndroidViteHmrReverse(device, projectData, liveSyncInfo, "pre-build");
|
|
289
289
|
const prepareResultData = await this.$prepareController.prepare(prepareData);
|
|
290
290
|
const buildData = {
|
|
291
291
|
...deviceDescriptor.buildData,
|
|
@@ -336,6 +336,11 @@ class RunController extends events_1.EventEmitter {
|
|
|
336
336
|
watch: !skipWatcher,
|
|
337
337
|
liveSyncDeviceData: deviceDescriptor,
|
|
338
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");
|
|
339
344
|
await this.refreshApplication(projectData, liveSyncResultInfo, null, deviceDescriptor);
|
|
340
345
|
this.$logger.info(`Successfully synced application ${liveSyncResultInfo.deviceAppData.appIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}.`);
|
|
341
346
|
this.emitCore(constants_2.RunOnDeviceEvents.runOnDeviceStarted, {
|
|
@@ -382,7 +387,7 @@ class RunController extends events_1.EventEmitter {
|
|
|
382
387
|
* The bundler then falls back to its own (now hardened) adb path, or
|
|
383
388
|
* ultimately to `10.0.2.2`, so a reverse hiccup never fails the run.
|
|
384
389
|
*/
|
|
385
|
-
async setupAndroidViteHmrReverse(device, projectData, liveSyncInfo) {
|
|
390
|
+
async setupAndroidViteHmrReverse(device, projectData, liveSyncInfo, phase) {
|
|
386
391
|
try {
|
|
387
392
|
if (!this.$mobileHelper.isAndroidPlatform(device.deviceInfo.platform)) {
|
|
388
393
|
return;
|
|
@@ -408,30 +413,73 @@ class RunController extends events_1.EventEmitter {
|
|
|
408
413
|
}
|
|
409
414
|
const serial = device.deviceInfo.identifier;
|
|
410
415
|
const port = this.getViteHmrPort();
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
+
}
|
|
431
446
|
}
|
|
432
447
|
catch (err) {
|
|
433
|
-
this.$logger.trace(`Setting up adb reverse for Vite HMR failed; leaving it to the bundler fallback. Error: ${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
|
+
}
|
|
434
481
|
}
|
|
482
|
+
return false;
|
|
435
483
|
}
|
|
436
484
|
getViteHmrPort() {
|
|
437
485
|
// The Vite dev server defaults to 5173; the bundler reads the same
|
|
@@ -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 can surface a clear log once the
|
|
451
|
+
// device can actually reach modules.
|
|
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.trace(`Vite dev server port ${port} not observed open within the readiness probe window; continuing (it may bind shortly).`);
|
|
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
|
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.5",
|
|
5
5
|
"author": "NativeScript <oss@nativescript.org>",
|
|
6
6
|
"description": "Command-line interface for building NativeScript projects",
|
|
7
7
|
"bin": {
|