Skip to content

Commit 2746bff

Browse files
Fix livesync/run issue (#2508)
* Fix livesync when directories are modified In case you try adding/removing directories in your `app` dir, `chokidar` (the file system watcher that we are using now instead of `gaze`) raises `addDir`/`unlinkDir` events. However we do not have handles for these events, so we do not execute anything. After that, when we add a file in the newly created dir, `chokidar` sends `add` event, we handle it and try to process the file. This works fine for iOS and Android devices, but it does not work at all for iOS Simulator, as we have not transferred the new directory to `platforms` dir. Add required handles for `addDir` and `unlinkDir` methods. Also currently there's a problem when already existing directory is renamed. In this case its modified time (`mtime`) is not changed, so the projectChangesService disregards the change and doesn't transfer it to `platforms` dir. After that, in case you modify any file inside the renamed dir, you'll see ENOENT error. In order to fix this, check the time of last status change (`ctime`) of the directory/file. * LiveSync only existing files during `--watch` During `--watch`, when a change is detected, the project is prepared and after that we are trying to move the prepared file (from `platforms/<platform>/...` dir) to the device. However some plugins modify the content of the `platforms` directory on afterPrepare. For example `nativescript-dev-sass` allows you to use `.scss` files, on `beforePrepare` they are "transpiled" to `.css` files. After that, on `afterPrepare` the plugin itself removes the file from `platforms/<platform>/...` dir. CLI detects the change in `.scss` file, prepares the project (which moves the `.scss` file to `platforms` dir) and after that tries to move it from `platforms` to the device. During the last step, the `.scss` file is already removed from `platforms` directory, so our code fails. Meanwhile, the `beforePrepare` hook of the plugin has created/modified the `.css` file inside `app` dir. This will trigger new iteration, where the file will be sent to device. In order to fix the error when the `.scss` file is modified, we'll execute livesync only if the modified files exist in `platforms` dir.
1 parent 20767d0 commit 2746bff

File tree

4 files changed

+38
-13
lines changed

4 files changed

+38
-13
lines changed

lib/services/livesync/livesync-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class LiveSyncService implements ILiveSyncService {
138138
fiberBootstrap.run(() => {
139139
that.$dispatcher.dispatch(() => (() => {
140140
try {
141+
that.$logger.trace(`Event '${event}' triggered for path: '${filePath}'`);
141142
filePath = path.join(syncWorkingDirectory, filePath);
142143
for (let i = 0; i < onChangedActions.length; i++) {
143144
onChangedActions[i](event, filePath, that.$dispatcher);

lib/services/livesync/platform-livesync-service.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe
4747
}
4848

4949
if (postAction) {
50-
this.finishLivesync(deviceAppData).wait();
51-
return postAction(deviceAppData).wait();
50+
this.finishLivesync(deviceAppData).wait();
51+
return postAction(deviceAppData).wait();
5252
}
5353

5454
this.refreshApplication(deviceAppData, localToDevicePaths, true).wait();
@@ -65,9 +65,9 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe
6565
return;
6666
}
6767

68-
if (event === "add" || event === "change") {
68+
if (event === "add" || event === "addDir" || event === "change") {
6969
this.batchSync(filePath, dispatcher, afterFileSyncAction);
70-
} else if (event === "unlink") {
70+
} else if (event === "unlink" || event === "unlinkDir") {
7171
this.syncRemovedFile(filePath, afterFileSyncAction).wait();
7272
}
7373
}
@@ -133,7 +133,7 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe
133133
try {
134134
for (let platform in this.batch) {
135135
let batch = this.batch[platform];
136-
batch.syncFiles(((filesToSync:string[]) => {
136+
batch.syncFiles(((filesToSync: string[]) => {
137137
this.$platformService.preparePlatform(this.liveSyncData.platform).wait();
138138
let canExecute = this.getCanExecuteAction(this.liveSyncData.platform, this.liveSyncData.appIdentifier);
139139
let deviceFileAction = (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, !filePath);
@@ -142,7 +142,7 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe
142142
}).future<void>()).wait();
143143
}
144144
} catch (err) {
145-
this.$logger.warn(`Unable to sync files. Error is:`, err.message);
145+
this.$logger.warn(`Unable to sync files. Error is:`, err.message);
146146
}
147147
}).future<void>()());
148148
}).future<void>()();
@@ -173,8 +173,8 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe
173173
afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture<void>): (device: Mobile.IDevice) => IFuture<void> {
174174
let action = (device: Mobile.IDevice): IFuture<void> => {
175175
return (() => {
176-
let deviceAppData:Mobile.IDeviceAppData = null;
177-
let localToDevicePaths:Mobile.ILocalToDevicePathData[] = null;
176+
let deviceAppData: Mobile.IDeviceAppData = null;
177+
let localToDevicePaths: Mobile.ILocalToDevicePathData[] = null;
178178
let isFullSync = false;
179179
if (this.$options.clean || this.$projectChangesService.currentChanges.changesRequireBuild) {
180180
let buildConfig: IBuildConfig = { buildForDevice: !device.isEmulator };
@@ -187,13 +187,29 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe
187187
isFullSync = true;
188188
} else {
189189
deviceAppData = this.$deviceAppDataFactory.create(this.liveSyncData.appIdentifier, this.$mobileHelper.normalizePlatformName(this.liveSyncData.platform), device);
190-
let mappedFiles = filesToSync.map((file: string) => this.$projectFilesProvider.mapFilePath(file, device.deviceInfo.platform));
191-
localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, this.liveSyncData.projectFilesPath, mappedFiles, this.liveSyncData.excludedProjectDirsAndFiles);
190+
191+
const mappedFiles = filesToSync.map((file: string) => this.$projectFilesProvider.mapFilePath(file, device.deviceInfo.platform));
192+
193+
// Some plugins modify platforms dir on afterPrepare (check nativescript-dev-sass) - we want to sync only existing file.
194+
const existingFiles = mappedFiles.filter(m => this.$fs.exists(m));
195+
196+
this.$logger.trace("Will execute livesync for files: ", existingFiles);
197+
198+
const skippedFiles = _.difference(mappedFiles, existingFiles);
199+
200+
if (skippedFiles.length) {
201+
this.$logger.trace("The following files will not be synced as they do not exist:", skippedFiles);
202+
}
203+
204+
localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, this.liveSyncData.projectFilesPath, existingFiles, this.liveSyncData.excludedProjectDirsAndFiles);
205+
192206
fileSyncAction(deviceAppData, localToDevicePaths).wait();
193207
}
208+
194209
if (!afterFileSyncAction) {
195210
this.refreshApplication(deviceAppData, localToDevicePaths, isFullSync).wait();
196211
}
212+
197213
device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(device.deviceInfo.platform), this.getLiveSyncInfoFilePath(deviceAppData), this.liveSyncData.appIdentifier).wait();
198214
this.finishLivesync(deviceAppData).wait();
199215
if (afterFileSyncAction) {
@@ -213,7 +229,7 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe
213229
let remoteLivesyncInfo: IPrepareInfo = JSON.parse(fileText);
214230
let localPrepareInfo = this.$projectChangesService.getPrepareInfo(platform);
215231
return remoteLivesyncInfo.time !== localPrepareInfo.time;
216-
} catch(e) {
232+
} catch (e) {
217233
return true;
218234
}
219235
}

lib/services/platform-service.ts

+3
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ export class PlatformService implements IPlatformService {
220220

221221
this.ensurePlatformInstalled(platform).wait();
222222
let changesInfo = this.$projectChangesService.checkForChanges(platform);
223+
224+
this.$logger.trace("Changes info in prepare platform:", changesInfo);
225+
223226
if (changesInfo.hasChanges) {
224227
// android build artifacts need to be cleaned up when switching from release to debug builds
225228
if (platform.toLowerCase() === "android") {

lib/services/project-changes-service.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,16 @@ export class ProjectChangesService implements IProjectChangesService {
176176
if (filePath === skipDir) {
177177
continue;
178178
}
179+
179180
let fileStats = this.$fs.getFsStats(filePath);
180-
let changed = fileStats.mtime.getTime() > this._outputProjectMtime;
181+
182+
let changed = fileStats.mtime.getTime() > this._outputProjectMtime || fileStats.ctime.getTime() > this._outputProjectMtime;
183+
181184
if (!changed) {
182185
let lFileStats = this.$fs.getLsStats(filePath);
183-
changed = lFileStats.mtime.getTime() > this._outputProjectMtime;
186+
changed = lFileStats.mtime.getTime() > this._outputProjectMtime || lFileStats.ctime.getTime() > this._outputProjectMtime;
184187
}
188+
185189
if (changed) {
186190
if (processFunc) {
187191
this._newFiles ++;
@@ -193,6 +197,7 @@ export class ProjectChangesService implements IProjectChangesService {
193197
return true;
194198
}
195199
}
200+
196201
if (fileStats.isDirectory()) {
197202
if (this.containsNewerFiles(filePath, skipDir, processFunc)) {
198203
return true;

0 commit comments

Comments
 (0)