Skip to content

Commit ea88148

Browse files
Merge pull request #2883 from NativeScript/vladimirov/new-livesync
Reimplement livesync
2 parents 2f3745a + b74d44d commit ea88148

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1840
-878
lines changed

PublicAPI.md

+203
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,32 @@ This document describes all methods that can be invoked when NativeScript CLI is
77
const tns = require("nativescript");
88
```
99

10+
# Contents
11+
* [projectService](#projectservice)
12+
* [createProject](#createproject)
13+
* [isValidNativeScriptProject](#isvalidnativescriptproject)
14+
* [extensibilityService](#extensibilityservice)
15+
* [installExtension](#installextension)
16+
* [uninstallExtension](#uninstallextension)
17+
* [getInstalledExtensions](#getinstalledextensions)
18+
* [loadExtensions](#loadextensions)
19+
* [settingsService](#settingsservice)
20+
* [setSettings](#setsettings)
21+
* [npm](#npm)
22+
* [install](#install)
23+
* [uninstall](#uninstall)
24+
* [search](#search)
25+
* [view](#view)
26+
* [analyticsService](#analyticsservice)
27+
* [startEqatecMonitor](#starteqatecmonitor)
28+
* [debugService](#debugservice)
29+
* [debug](#debug)
30+
* [liveSyncService](#livesyncservice)
31+
* [liveSync](#livesync)
32+
* [stopLiveSync](#stopLiveSync)
33+
* [events](#events)
34+
35+
1036
## Module projectService
1137

1238
`projectService` modules allow you to create new NativeScript application.
@@ -498,6 +524,183 @@ tns.debugService.debug(debugData, debugOptions)
498524
.catch(err => console.log(`Unable to start debug operation, reason: ${err.message}.`));
499525
```
500526
527+
## liveSyncService
528+
Used to LiveSync changes on devices. The operation can be started for multiple devices and stopped for each of them. During LiveSync operation, the service will emit different events based on the action that's executing.
529+
530+
### liveSync
531+
Starts a LiveSync operation for specified devices. During the operation, application may have to be rebuilt (for example in case a change in App_Resources is detected).
532+
By default the LiveSync operation will start file system watcher for `<project dir>/app` directory and any change in it will trigger a LiveSync operation.
533+
After calling the method once, you can add new devices to the same LiveSync operation by calling the method again with the new device identifiers.
534+
535+
> NOTE: Consecutive calls to `liveSync` method for the same project will execute the initial sync (deploy and fullSync) only for new device identifiers. So in case the first call is for devices with ids [ 'A' , 'B' ] and the second one is for devices with ids [ 'B', 'C' ], the initial sync will be executed only for device with identifier 'C'.
536+
537+
> NOTE: In case a consecutive call to `liveSync` method requires change in the pattern for watching files (i.e. `liveSyncData.syncAllFiles` option has changed), current watch operation will be stopped and a new one will be started.
538+
539+
* Definition
540+
```TypeScript
541+
/**
542+
* Starts LiveSync operation by rebuilding the application if necessary and starting watcher.
543+
* @param {ILiveSyncDeviceInfo[]} deviceDescriptors Describes each device for which we would like to sync the application - identifier, outputPath and action to rebuild the app.
544+
* @param {ILiveSyncInfo} liveSyncData Describes the LiveSync operation - for which project directory is the operation and other settings.
545+
* @returns {Promise<void>}
546+
*/
547+
liveSync(deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncData: ILiveSyncInfo): Promise<void>;
548+
```
549+
550+
* Usage:
551+
```JavaScript
552+
const projectDir = "myProjectDir";
553+
const androidDeviceDescriptor = {
554+
identifier: "4df18f307d8a8f1b",
555+
buildAction: () => {
556+
return tns.localBuildService.build("Android", { projectDir, bundle: false, release: false, buildForDevice: true });
557+
},
558+
outputPath: null
559+
};
560+
561+
const iOSDeviceDescriptor = {
562+
identifier: "12318af23ebc0e25",
563+
buildAction: () => {
564+
return tns.localBuildService.build("iOS", { projectDir, bundle: false, release: false, buildForDevice: true });
565+
},
566+
outputPath: null
567+
};
568+
569+
const liveSyncData = {
570+
projectDir,
571+
skipWatcher: false,
572+
watchAllFiles: false,
573+
useLiveEdit: false
574+
};
575+
576+
tns.liveSyncService.liveSync([ androidDeviceDescriptor, iOSDeviceDescriptor ], liveSyncData)
577+
.then(() => {
578+
console.log("LiveSync operation started.");
579+
}, err => {
580+
console.log("An error occurred during LiveSync", err);
581+
});
582+
```
583+
584+
### stopLiveSync
585+
Stops LiveSync operation. In case deviceIdentifires are passed, the operation will be stopped only for these devices.
586+
587+
* Definition
588+
```TypeScript
589+
/**
590+
* Stops LiveSync operation for specified directory.
591+
* @param {string} projectDir The directory for which to stop the operation.
592+
* @param {string[]} @optional deviceIdentifiers Device ids for which to stop the application. In case nothing is passed, LiveSync operation will be stopped for all devices.
593+
* @returns {Promise<void>}
594+
*/
595+
stopLiveSync(projectDir: string, deviceIdentifiers?: string[]): Promise<void>;
596+
```
597+
598+
* Usage
599+
```JavaScript
600+
const projectDir = "myProjectDir";
601+
const deviceIdentifiers = [ "4df18f307d8a8f1b", "12318af23ebc0e25" ];
602+
tns.liveSyncService.stopLiveSync(projectDir, deviceIdentifiers)
603+
.then(() => {
604+
console.log("LiveSync operation stopped.");
605+
}, err => {
606+
console.log("An error occurred during stopage.", err);
607+
});
608+
```
609+
610+
### Events
611+
`liveSyncService` raises several events in order to provide information for current state of the operation.
612+
* liveSyncStarted - raised whenever CLI starts a LiveSync operation for specific device. When `liveSync` method is called, the initial LiveSync operation will emit `liveSyncStarted` for each specified device. After that the event will be emitted only in case when liveSync method is called again with different device instances. The event is raised with the following data:
613+
```TypeScript
614+
{
615+
projectDir: string;
616+
deviceIdentifier: string;
617+
applicationIdentifier: string;
618+
}
619+
```
620+
621+
Example:
622+
```JavaScript
623+
tns.liveSyncService.on("liveSyncStarted", data => {
624+
console.log(`Started LiveSync on ${data.deviceIdentifier} for ${data.applicationIdentifier}.`);
625+
});
626+
```
627+
628+
* liveSyncExecuted - raised whenever CLI finishes a LiveSync operation for specific device. When `liveSync` method is called, the initial LiveSync operation will emit `liveSyncExecuted` for each specified device once it finishes the operation. After that the event will be emitted whenever a change is detected (in case file system watcher is started) and the LiveSync operation is executed for each device. The event is raised with the following data:
629+
```TypeScript
630+
{
631+
projectDir: string;
632+
deviceIdentifier: string;
633+
applicationIdentifier: string;
634+
/**
635+
* Full paths to files synced during the operation. In case the `syncedFiles.length` is 0, the operation is "fullSync" (i.e. all project files are synced).
636+
*/
637+
syncedFiles: string[];
638+
}
639+
```
640+
641+
Example:
642+
```JavaScript
643+
tns.liveSyncService.on("liveSyncExecuted", data => {
644+
console.log(`Executed LiveSync on ${data.deviceIdentifier} for ${data.applicationIdentifier}. Uploaded files are: ${data.syncedFiles.join(" ")}.`);
645+
});
646+
```
647+
648+
* liveSyncStopped - raised when LiveSync operation is stopped. The event will be raised when the operation is stopped for each device and will be raised when the whole operation is stopped. The event is raised with the following data:
649+
```TypeScript
650+
{
651+
projectDir: string;
652+
/**
653+
* Passed only when the LiveSync operation is stopped for a specific device. In case it is not passed, the whole LiveSync operation is stopped.
654+
*/
655+
deviceIdentifier?: string;
656+
}
657+
```
658+
659+
Example:
660+
```JavaScript
661+
tns.liveSyncService.on("liveSyncStopped", data => {
662+
if (data.deviceIdentifier) {
663+
console.log(`Stopped LiveSync on ${data.deviceIdentifier} for ${data.projectDir}.`);
664+
} else {
665+
console.log(`Stopped LiveSync for ${data.projectDir}.`);
666+
}
667+
});
668+
```
669+
670+
* liveSyncError - raised whenever an error is detected during LiveSync operation. The event is raised for specific device. Once an error is detected, the event will be raised and the LiveSync operation will be stopped for this device, i.e. `liveSyncStopped` event will be raised for it. The event is raised with the following data:
671+
```TypeScript
672+
{
673+
projectDir: string;
674+
deviceIdentifier: string;
675+
applicationIdentifier: string;
676+
error: Error;
677+
}
678+
```
679+
680+
Example:
681+
```JavaScript
682+
tns.liveSyncService.on("liveSyncError", data => {
683+
console.log(`Error detected during LiveSync on ${data.deviceIdentifier} for ${data.projectDir}. Error: ${data.error.message}.`);
684+
});
685+
```
686+
687+
* notify - raised when LiveSync operation has some data that is important for the user. The event is raised for specific device. The event is raised with the following data:
688+
```TypeScript
689+
{
690+
projectDir: string;
691+
deviceIdentifier: string;
692+
applicationIdentifier: string;
693+
notification: string;
694+
}
695+
```
696+
697+
Example:
698+
```JavaScript
699+
tns.liveSyncService.on("notify", data => {
700+
console.log(`Notification: ${data.notification} for LiveSync operation on ${data.deviceIdentifier} for ${data.projectDir}. `);
701+
});
702+
```
703+
501704
## How to add a new method to Public API
502705
CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification.
503706
For example the `$options` injected module contains information about all `--` options passed on the terminal. When the CLI is used as a library, the options are not populated. Before adding method to public API, make sure its implementation does not rely on `$options`.

lib/bootstrap.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ $injector.require("platformsData", "./platforms-data");
2424
$injector.require("platformService", "./services/platform-service");
2525

2626
$injector.require("debugDataService", "./services/debug-data-service");
27+
$injector.requirePublicClass("debugService", "./services/debug-service");
2728
$injector.require("iOSDebugService", "./services/ios-debug-service");
2829
$injector.require("androidDebugService", "./services/android-debug-service");
2930

@@ -39,6 +40,7 @@ $injector.requireCommand("platform|*list", "./commands/list-platforms");
3940
$injector.requireCommand("platform|add", "./commands/add-platform");
4041
$injector.requireCommand("platform|remove", "./commands/remove-platform");
4142
$injector.requireCommand("platform|update", "./commands/update-platform");
43+
$injector.requireCommand("run|*all", "./commands/run");
4244
$injector.requireCommand("run|ios", "./commands/run");
4345
$injector.requireCommand("run|android", "./commands/run");
4446

@@ -74,7 +76,6 @@ $injector.require("commandsServiceProvider", "./providers/commands-service-provi
7476
$injector.require("deviceAppDataProvider", "./providers/device-app-data-provider");
7577

7678
$injector.require("deviceLogProvider", "./common/mobile/device-log-provider");
77-
$injector.require("liveSyncProvider", "./providers/livesync-provider");
7879
$injector.require("projectFilesProvider", "./providers/project-files-provider");
7980

8081
$injector.require("nodeModulesBuilder", "./tools/node-modules/node-modules-builder");
@@ -99,14 +100,15 @@ $injector.require("infoService", "./services/info-service");
99100
$injector.requireCommand("info", "./commands/info");
100101

101102
$injector.require("androidToolsInfo", "./android-tools-info");
103+
$injector.require("devicePathProvider", "./device-path-provider");
102104

103105
$injector.requireCommand("platform|clean", "./commands/platform-clean");
104106

107+
$injector.requirePublicClass("liveSyncService", "./services/livesync/livesync-service");
108+
$injector.require("debugLiveSyncService", "./services/livesync/debug-livesync-service");
109+
$injector.require("androidLiveSyncService", "./services/livesync/android-livesync-service");
110+
$injector.require("iOSLiveSyncService", "./services/livesync/ios-livesync-service");
105111
$injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); // The name is used in https://github.com/NativeScript/nativescript-dev-typescript
106-
$injector.require("iosLiveSyncServiceLocator", "./services/livesync/ios-device-livesync-service");
107-
$injector.require("androidLiveSyncServiceLocator", "./services/livesync/android-device-livesync-service");
108-
$injector.require("platformLiveSyncService", "./services/livesync/platform-livesync-service");
109-
110112
$injector.require("sysInfo", "./sys-info");
111113

112114
$injector.require("iOSNotificationService", "./services/ios-notification-service");

lib/commands/appstore-list.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,19 @@ export class ListiOSApps implements ICommand {
77
constructor(private $injector: IInjector,
88
private $itmsTransporterService: IITMSTransporterService,
99
private $logger: ILogger,
10-
private $prompter: IPrompter) { }
10+
private $projectData: IProjectData,
11+
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
12+
private $platformService: IPlatformService,
13+
private $errors: IErrors,
14+
private $prompter: IPrompter) {
15+
this.$projectData.initializeProjectData();
16+
}
1117

1218
public async execute(args: string[]): Promise<void> {
19+
if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) {
20+
this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`);
21+
}
22+
1323
let username = args[0],
1424
password = args[1];
1525

lib/commands/appstore-upload.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export class PublishIOS implements ICommand {
77
new StringCommandParameter(this.$injector), new StringCommandParameter(this.$injector)];
88

99
constructor(private $errors: IErrors,
10-
private $hostInfo: IHostInfo,
1110
private $injector: IInjector,
1211
private $itmsTransporterService: IITMSTransporterService,
1312
private $logger: ILogger,
@@ -100,8 +99,8 @@ export class PublishIOS implements ICommand {
10099
}
101100

102101
public async canExecute(args: string[]): Promise<boolean> {
103-
if (!this.$hostInfo.isDarwin) {
104-
this.$errors.failWithoutHelp("This command is only available on Mac OS X.");
102+
if (!this.$platformService.isPlatformSupportedForOS(this.$devicePlatformsConstants.iOS, this.$projectData)) {
103+
this.$errors.fail(`Applications for platform ${this.$devicePlatformsConstants.iOS} can not be built on this OS`);
105104
}
106105

107106
return true;

lib/commands/build.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
export class BuildCommandBase {
22
constructor(protected $options: IOptions,
3+
protected $errors: IErrors,
34
protected $projectData: IProjectData,
45
protected $platformsData: IPlatformsData,
6+
protected $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
57
protected $platformService: IPlatformService) {
68
this.$projectData.initializeProjectData();
79
}
@@ -29,23 +31,32 @@ export class BuildCommandBase {
2931
this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData);
3032
}
3133
}
34+
35+
protected validatePlatform(platform: string): void {
36+
if (!this.$platformService.isPlatformSupportedForOS(platform, this.$projectData)) {
37+
this.$errors.fail(`Applications for platform ${platform} can not be built on this OS`);
38+
}
39+
}
3240
}
3341

3442
export class BuildIosCommand extends BuildCommandBase implements ICommand {
3543
public allowedParameters: ICommandParameter[] = [];
3644

3745
constructor(protected $options: IOptions,
46+
$errors: IErrors,
3847
$projectData: IProjectData,
3948
$platformsData: IPlatformsData,
49+
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
4050
$platformService: IPlatformService) {
41-
super($options, $projectData, $platformsData, $platformService);
51+
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService);
4252
}
4353

4454
public async execute(args: string[]): Promise<void> {
4555
return this.executeCore([this.$platformsData.availablePlatforms.iOS]);
4656
}
4757

4858
public canExecute(args: string[]): Promise<boolean> {
59+
super.validatePlatform(this.$devicePlatformsConstants.iOS);
4960
return args.length === 0 && this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.iOS);
5061
}
5162
}
@@ -56,18 +67,20 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand {
5667
public allowedParameters: ICommandParameter[] = [];
5768

5869
constructor(protected $options: IOptions,
70+
protected $errors: IErrors,
5971
$projectData: IProjectData,
6072
$platformsData: IPlatformsData,
61-
private $errors: IErrors,
73+
$devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
6274
$platformService: IPlatformService) {
63-
super($options, $projectData, $platformsData, $platformService);
75+
super($options, $errors, $projectData, $platformsData, $devicePlatformsConstants, $platformService);
6476
}
6577

6678
public async execute(args: string[]): Promise<void> {
6779
return this.executeCore([this.$platformsData.availablePlatforms.Android]);
6880
}
6981

7082
public async canExecute(args: string[]): Promise<boolean> {
83+
super.validatePlatform(this.$devicePlatformsConstants.Android);
7184
if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) {
7285
this.$errors.fail("When producing a release build, you need to specify all --key-store-* options.");
7386
}

0 commit comments

Comments
 (0)