-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathlivesync-service.ts
160 lines (130 loc) · 6.75 KB
/
livesync-service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import * as constants from "../../constants";
import * as helpers from "../../common/helpers";
import * as path from "path";
import * as semver from "semver";
import { NodeModulesDependenciesBuilder } from "../../tools/node-modules/node-modules-dependencies-builder";
let choki = require("chokidar");
class LiveSyncService implements ILiveSyncService {
private _isInitialized = false;
constructor(private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $errors: IErrors,
private $platformsData: IPlatformsData,
private $platformService: IPlatformService,
private $projectData: IProjectData,
private $projectDataService: IProjectDataService,
private $prompter: IPrompter,
private $injector: IInjector,
private $mobileHelper: Mobile.IMobileHelper,
private $devicesService: Mobile.IDevicesService,
private $options: IOptions,
private $logger: ILogger,
private $dispatcher: IFutureDispatcher,
private $hooksService: IHooksService,
private $processService: IProcessService) { }
private async ensureAndroidFrameworkVersion(platformData: IPlatformData): Promise<void> { // TODO: this can be moved inside command or canExecute function
this.$projectDataService.initialize(this.$projectData.projectDir);
let frameworkVersion = this.$projectDataService.getValue(platformData.frameworkPackageName).version;
if (platformData.normalizedPlatformName.toLowerCase() === this.$devicePlatformsConstants.Android.toLowerCase()) {
if (semver.lt(frameworkVersion, "1.2.1")) {
let shouldUpdate = await this.$prompter.confirm("You need Android Runtime 1.2.1 or later for LiveSync to work properly. Do you want to update your runtime now?");
if (shouldUpdate) {
await this.$platformService.updatePlatforms([this.$devicePlatformsConstants.Android.toLowerCase()]);
} else {
return;
}
}
}
}
public get isInitialized(): boolean { // This function is used from https://github.com/NativeScript/nativescript-dev-typescript/blob/master/lib/before-prepare.js#L4
return this._isInitialized;
}
public async liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise<void>): Promise<void> {
if (this.$options.justlaunch) {
this.$options.watch = false;
}
let liveSyncData: ILiveSyncData[] = [];
if (platform) {
await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device });
liveSyncData.push(await this.prepareLiveSyncData(platform));
} else if (this.$options.device) {
await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device });
platform = this.$devicesService.getDeviceByIdentifier(this.$options.device).deviceInfo.platform;
liveSyncData.push(await this.prepareLiveSyncData(platform));
} else {
await this.$devicesService.initialize({ skipInferPlatform: true });
await this.$devicesService.stopDeviceDetectionInterval();
for (let installedPlatform of this.$platformService.getInstalledPlatforms()) {
if (this.$devicesService.getDevicesForPlatform(installedPlatform).length === 0) {
await this.$devicesService.startEmulator(installedPlatform);
}
liveSyncData.push(await this.prepareLiveSyncData(installedPlatform));
}
}
if (liveSyncData.length === 0) {
this.$errors.fail("There are no platforms installed in this project. Please specify platform or install one by using `tns platform add` command!");
}
this._isInitialized = true; // If we want before-prepare hooks to work properly, this should be set after preparePlatform function
await this.liveSyncCore(liveSyncData, applicationReloadAction);
}
private async prepareLiveSyncData(platform: string): Promise<ILiveSyncData> {
platform = platform || this.$devicesService.platform;
let platformData = this.$platformsData.getPlatformData(platform.toLowerCase());
if (this.$mobileHelper.isAndroidPlatform(platform)) {
await this.ensureAndroidFrameworkVersion(platformData);
}
let liveSyncData: ILiveSyncData = {
platform: platform,
appIdentifier: this.$projectData.projectId,
projectFilesPath: path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME),
syncWorkingDirectory: this.$projectData.projectDir,
excludedProjectDirsAndFiles: this.$options.release ? constants.LIVESYNC_EXCLUDED_FILE_PATTERNS : []
};
return liveSyncData;
}
@helpers.hook('livesync')
private async liveSyncCore(liveSyncData: ILiveSyncData[], applicationReloadAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise<void>): Promise<void> {
await this.$platformService.trackProjectType();
let watchForChangeActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => Promise<void>)[] = [];
for (let dataItem of liveSyncData) {
let service: IPlatformLiveSyncService = this.$injector.resolve("platformLiveSyncService", { _liveSyncData: dataItem });
watchForChangeActions.push((event: string, filePath: string, dispatcher: IFutureDispatcher) =>
service.partialSync(event, filePath, dispatcher, applicationReloadAction));
await service.fullSync(applicationReloadAction);
}
if (this.$options.watch && !this.$options.justlaunch) {
await this.$hooksService.executeBeforeHooks('watch');
await this.partialSync(liveSyncData[0].syncWorkingDirectory, watchForChangeActions);
}
}
private partialSync(syncWorkingDirectory: string, onChangedActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => Promise<void>)[]): void {
let that = this;
let dependenciesBuilder = this.$injector.resolve(NodeModulesDependenciesBuilder, {});
let productionDependencies = dependenciesBuilder.getProductionDependencies(this.$projectData.projectDir);
let pattern = ["app"];
if (this.$options.syncAllFiles) {
pattern.push("package.json");
// watch only production node_module/packages same one prepare uses
for (let index in productionDependencies) {
pattern.push("node_modules/" + productionDependencies[index].name);
}
}
let watcher = choki.watch(pattern, { ignoreInitial: true, cwd: syncWorkingDirectory, ignored: '**/*.DS_Store' }).on("all", (event: string, filePath: string) => {
that.$dispatcher.dispatch(async () => {
try {
filePath = path.join(syncWorkingDirectory, filePath);
for (let i = 0; i < onChangedActions.length; i++) {
await onChangedActions[i](event, filePath, that.$dispatcher);
}
} catch (err) {
that.$logger.info(`Unable to sync file ${filePath}. Error is:${err.message}`.red.bold);
that.$logger.info("Try saving it again or restart the livesync operation.");
}
});
});
this.$processService.attachToProcessExitSignals(this, () => {
watcher.close(pattern);
});
this.$dispatcher.run();
}
}
$injector.register("usbLiveSyncService", LiveSyncService);