Skip to content

Commit

Permalink
Start livesync watcher before preparing the project
Browse files Browse the repository at this point in the history
Implemented #3404
  • Loading branch information
Fatme committed May 11, 2018
1 parent acb7030 commit 82364e4
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 22 deletions.
2 changes: 2 additions & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,5 @@ $injector.require("nativeScriptCloudExtensionService", "./services/nativescript-
$injector.requireCommand("resources|generate|icons", "./commands/generate-assets");
$injector.requireCommand("resources|generate|splashes", "./commands/generate-assets");
$injector.requirePublic("assetsGenerationService", "./services/assets-generation/assets-generation-service");

$injector.require("filesHashService", "./services/files-hash-service");
4 changes: 4 additions & 0 deletions lib/definitions/files-hash-service.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface IFilesHashService {
generateHashes(files: string[]): Promise<IStringDictionary>;
getChanges(files: string[], oldHashes: IStringDictionary): Promise<IStringDictionary>;
}
4 changes: 2 additions & 2 deletions lib/definitions/platform.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ interface IPlatformService extends IBuildPlatformAction, NodeJS.EventEmitter {
* @param {IShouldPrepareInfo} shouldPrepareInfo Options needed to decide whether to prepare.
* @returns {Promise<boolean>} true indicates that the project should be prepared.
*/
shouldPrepare(shouldPrepareInfo: IShouldPrepareInfo): Promise<boolean>
shouldPrepare(shouldPrepareInfo: IShouldPrepareInfo): Promise<boolean>;

/**
* Installs the application on specified device.
Expand Down Expand Up @@ -213,7 +213,7 @@ interface IPlatformService extends IBuildPlatformAction, NodeJS.EventEmitter {
* @param {string} buildInfoFileDirname The directory where the build file should be written to.
* @returns {void}
*/
saveBuildInfoFile(platform: string, projectDir: string, buildInfoFileDirname: string): void
saveBuildInfoFile(platform: string, projectDir: string, buildInfoFileDirname: string): void;
}

interface IPlatformOptions extends IPlatformSpecificData, ICreateProjectOptions { }
Expand Down
6 changes: 5 additions & 1 deletion lib/definitions/project-changes.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
interface IPrepareInfo extends IAddedNativePlatform {
interface IAppFilesHashes {
appFilesHashes: IStringDictionary;
}

interface IPrepareInfo extends IAddedNativePlatform, IAppFilesHashes {
time: string;
bundle: boolean;
release: boolean;
Expand Down
26 changes: 26 additions & 0 deletions lib/services/files-hash-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { executeActionByChunks } from "../common/helpers";
import { DEFAULT_CHUNK_SIZE } from "../common/constants";

export class FilesHashService implements IFilesHashService {
constructor(private $fs: IFileSystem) { }

public async generateHashes(files: string[]): Promise<IStringDictionary> {
const result: IStringDictionary = {};

const action = async (file: string) => {
if (this.$fs.getFsStats(file).isFile()) {
result[file] = await this.$fs.getFileShasum(file);
}
};

await executeActionByChunks(files, DEFAULT_CHUNK_SIZE, action);

return result;
}

public async getChanges(files: string[], oldHashes: IStringDictionary): Promise<IStringDictionary> {
const newHashes = await this.generateHashes(files);
return _.omitBy(newHashes, (hash: string, pathToFile: string) => !!_.find(oldHashes, (oldHash: string, oldPath: string) => pathToFile === oldPath && hash === oldHash));
}
}
$injector.register("filesHashService", FilesHashService);
31 changes: 17 additions & 14 deletions lib/services/livesync/livesync-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,26 +318,16 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
}

@hook("liveSync")
private async liveSyncOperation(deviceDescriptors: ILiveSyncDeviceInfo[],
liveSyncData: ILiveSyncInfo, projectData: IProjectData): Promise<void> {
private async liveSyncOperation(deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncData: ILiveSyncInfo, projectData: IProjectData): Promise<void> {
// In case liveSync is called for a second time for the same projectDir.
const isAlreadyLiveSyncing = this.liveSyncProcessesInfo[projectData.projectDir] && !this.liveSyncProcessesInfo[projectData.projectDir].isStopped;

// Prevent cases where liveSync is called consecutive times with the same device, for example [ A, B, C ] and then [ A, B, D ] - we want to execute initialSync only for D.
const currentlyRunningDeviceDescriptors = this.getLiveSyncDeviceDescriptors(projectData.projectDir);
const deviceDescriptorsForInitialSync = isAlreadyLiveSyncing ? _.differenceBy(deviceDescriptors, currentlyRunningDeviceDescriptors, deviceDescriptorPrimaryKey) : deviceDescriptors;
this.setLiveSyncProcessInfo(liveSyncData.projectDir, deviceDescriptors);

await this.initialSync(projectData, deviceDescriptorsForInitialSync, liveSyncData);

if (!liveSyncData.skipWatcher && this.liveSyncProcessesInfo[projectData.projectDir].deviceDescriptors.length) {
// Should be set after prepare
this.$usbLiveSyncService.isInitialized = true;

const devicesIds = deviceDescriptors.map(dd => dd.identifier);
const devices = _.filter(this.$devicesService.getDeviceInstances(), device => _.includes(devicesIds, device.deviceInfo.identifier));
const platforms = _(devices).map(device => device.deviceInfo.platform).uniq().value();
await this.startWatcher(projectData, liveSyncData, platforms);
await this.startWatcher(projectData, liveSyncData, deviceDescriptors, { isAlreadyLiveSyncing });
}
}

Expand All @@ -351,6 +341,13 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
this.liveSyncProcessesInfo[projectDir].deviceDescriptors = _.uniqBy(currentDeviceDescriptors.concat(deviceDescriptors), deviceDescriptorPrimaryKey);
}

private async initialSync(projectData: IProjectData, liveSyncData: ILiveSyncInfo, deviceDescriptors: ILiveSyncDeviceInfo[], options: { isAlreadyLiveSyncing: boolean }): Promise<void> {
// Prevent cases where liveSync is called consecutive times with the same device, for example [ A, B, C ] and then [ A, B, D ] - we want to execute initialSync only for D.
const currentlyRunningDeviceDescriptors = this.getLiveSyncDeviceDescriptors(projectData.projectDir);
const deviceDescriptorsForInitialSync = options.isAlreadyLiveSyncing ? _.differenceBy(deviceDescriptors, currentlyRunningDeviceDescriptors, deviceDescriptorPrimaryKey) : deviceDescriptors;
await this.initialSyncCore(projectData, deviceDescriptorsForInitialSync, liveSyncData);
}

private getLiveSyncService(platform: string): IPlatformLiveSyncService {
if (this.$mobileHelper.isiOSPlatform(platform)) {
return this.$injector.resolve("iOSLiveSyncService");
Expand Down Expand Up @@ -452,7 +449,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
return null;
}

private async initialSync(projectData: IProjectData, deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncData: ILiveSyncInfo): Promise<void> {
private async initialSyncCore(projectData: IProjectData, deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncData: ILiveSyncInfo): Promise<void> {
const preparedPlatforms: string[] = [];
const rebuiltInformation: ILiveSyncBuildInfo[] = [];

Expand Down Expand Up @@ -483,6 +480,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
useLiveEdit: liveSyncData.useLiveEdit,
watch: !liveSyncData.skipWatcher
});

await this.$platformService.trackActionForPlatform({ action: "LiveSync", platform: device.deviceInfo.platform, isForDevice: !device.isEmulator, deviceOsVersion: device.deviceInfo.version });
await this.refreshApplication(projectData, liveSyncResultInfo, deviceBuildInfoDescriptor.debugOptions, deviceBuildInfoDescriptor.outputPath);

Expand Down Expand Up @@ -525,7 +523,10 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
};
}

private async startWatcher(projectData: IProjectData, liveSyncData: ILiveSyncInfo, platforms: string[]): Promise<void> {
private async startWatcher(projectData: IProjectData, liveSyncData: ILiveSyncInfo, deviceDescriptors: ILiveSyncDeviceInfo[], options: { isAlreadyLiveSyncing: boolean }): Promise<void> {
const devicesIds = deviceDescriptors.map(dd => dd.identifier);
const devices = _.filter(this.$devicesService.getDeviceInstances(), device => _.includes(devicesIds, device.deviceInfo.identifier));
const platforms = _(devices).map(device => device.deviceInfo.platform).uniq().value();
const patterns = await this.getWatcherPatterns(liveSyncData, projectData, platforms);

if (liveSyncData.watchAllFiles) {
Expand Down Expand Up @@ -683,6 +684,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
this.liveSyncProcessesInfo[liveSyncData.projectDir].watcherInfo = { watcher, patterns };
this.liveSyncProcessesInfo[liveSyncData.projectDir].timer = timeoutTimer;

await this.initialSync(projectData, liveSyncData, deviceDescriptors, options);

this.$processService.attachToProcessExitSignals(this, () => {
_.keys(this.liveSyncProcessesInfo).forEach(projectDir => {
// Do not await here, we are in process exit's handler.
Expand Down
23 changes: 19 additions & 4 deletions lib/services/project-changes-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from "path";
import { NODE_MODULES_FOLDER_NAME, NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME } from "../constants";
import { NODE_MODULES_FOLDER_NAME, NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, APP_RESOURCES_FOLDER_NAME } from "../constants";
import { getHash } from "../common/helpers";

const prepareInfoFileName = ".nsprepareinfo";
Expand Down Expand Up @@ -48,7 +48,8 @@ export class ProjectChangesService implements IProjectChangesService {
constructor(
private $platformsData: IPlatformsData,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $fs: IFileSystem) {
private $fs: IFileSystem,
private $filesHashService: IFilesHashService) {
}

public get currentChanges(): IProjectChangesInfo {
Expand All @@ -60,7 +61,10 @@ export class ProjectChangesService implements IProjectChangesService {
this._changesInfo = new ProjectChangesInfo();
if (!this.ensurePrepareInfo(platform, projectData, projectChangesOptions)) {
this._newFiles = 0;
this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData);

const appFiles = this.$fs.enumerateFilesInDirectorySync(projectData.appDirectoryPath, (filePath: string, stat: IFsStats) => filePath.indexOf(APP_RESOURCES_FOLDER_NAME) === -1);
this._changesInfo.appFilesChanged = await this.hasChangedFiles(appFiles, projectData);

this._changesInfo.packageChanged = this.isProjectFileChanged(projectData, platform);
this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.appResourcesDirectoryPath, null, projectData);
/*done because currently all node_modules are traversed, a possible improvement could be traversing only the production dependencies*/
Expand Down Expand Up @@ -173,7 +177,8 @@ export class ProjectChangesService implements IProjectChangesService {
release: projectChangesOptions.release,
changesRequireBuild: true,
projectFileHash: this.getProjectFileStrippedHash(projectData, platform),
changesRequireBuildTime: null
changesRequireBuildTime: null,
appFilesHashes: {}
};

this._outputProjectMtime = 0;
Expand Down Expand Up @@ -300,5 +305,15 @@ export class ProjectChangesService implements IProjectChangesService {
}
return false;
}

private async hasChangedFiles(files: string[], projectData: IProjectData): Promise<boolean> {
const changedFiles = await this.$filesHashService.getChanges(files, this._prepareInfo.appFilesHashes || {});
const hasChanges = changedFiles && _.keys(changedFiles).length > 0;
if (hasChanges) {
this._prepareInfo.appFilesHashes = await this.$filesHashService.generateHashes(files);
}

return hasChanges;
}
}
$injector.register("projectChangesService", ProjectChangesService);
4 changes: 3 additions & 1 deletion test/project-changes-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ProjectChangesServiceTest extends BaseServiceTest {
this.injector.register("devicePlatformsConstants", {});
this.injector.register("devicePlatformsConstants", {});
this.injector.register("projectChangesService", ProjectChangesService);
this.injector.register("filesHashService", {});

const fs = this.injector.resolve<IFileSystem>("fs");
fs.writeJson(path.join(this.projectDir, Constants.PACKAGE_JSON_FILE_NAME), {
Expand Down Expand Up @@ -127,7 +128,8 @@ describe("Project Changes Service Tests", () => {
changesRequireBuildTime: new Date().toString(),
iOSProvisioningProfileUUID: "provisioning_profile_test",
projectFileHash: "",
nativePlatformStatus: Constants.NativePlatformStatus.requiresPlatformAdd
nativePlatformStatus: Constants.NativePlatformStatus.requiresPlatformAdd,
appFilesHashes: {}
};
fs.writeJson(prepareInfoPath, expectedPrepareInfo);

Expand Down

0 comments on commit 82364e4

Please sign in to comment.