diff --git a/lib/common b/lib/common index 67450b9350..c635c1a6ea 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 67450b93507cd7c535fb3f3f62a791626d3d7f4c +Subproject commit c635c1a6ea1baf36fcd1dfd6aac868940d1ad8e2 diff --git a/lib/constants.ts b/lib/constants.ts index 000205df55..d2a96c0d06 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -54,6 +54,7 @@ export class LiveSyncTrackActionNames { } export const PackageJsonKeysToKeep: Array = ["name", "main", "android", "version", "pluginsData"]; +export const TemplatesV2PackageJsonKeysToRemove: Array = ["name", "version", "displayName", "templateType", "author", "keywords", "homepage", "bugs"]; export class SaveOptions { static PRODUCTION = "save"; diff --git a/lib/definitions/livesync-global.d.ts b/lib/definitions/livesync-global.d.ts index ac93fae85a..ecbc48f0d9 100644 --- a/lib/definitions/livesync-global.d.ts +++ b/lib/definitions/livesync-global.d.ts @@ -1,7 +1,7 @@ -import * as stream from "stream"; +import { Socket } from "net"; declare global { - interface IDuplexSocket extends stream.Duplex { + interface INetSocket extends Socket { uid?: string; } } diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index d10d86fd43..4989f16056 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -332,6 +332,8 @@ interface ILiveSyncResultInfo { useLiveEdit?: boolean; } +interface IAndroidLiveSyncResultInfo extends ILiveSyncResultInfo, IAndroidLivesyncSyncOperationResult { } + interface IFullSyncInfo extends IProjectDataComposition { device: Mobile.IDevice; watch: boolean; @@ -377,22 +379,22 @@ interface INativeScriptDeviceLiveSyncService extends IDeviceLiveSyncServiceBase * @return {Promise} Returns the ILocalToDevicePathData of all transfered files */ transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): Promise; - - /** - * Guarantees all remove/update operations have finished - * @param {ILiveSyncResultInfo} liveSyncInfo Describes the LiveSync operation - for which project directory is the operation and other settings. - * @return {Promise} - */ - finalizeSync(liveSyncInfo: ILiveSyncResultInfo): Promise; } -interface IAndroidNativeScriptDeviceLiveSyncService { +interface IAndroidNativeScriptDeviceLiveSyncService extends INativeScriptDeviceLiveSyncService { /** * Retrieves the android device's hash service. * @param {string} appIdentifier Application identifier. * @return {Promise} The hash service */ getDeviceHashService(appIdentifier: string): Mobile.IAndroidDeviceHashService; + + /** + * Guarantees all remove/update operations have finished + * @param {ILiveSyncResultInfo} liveSyncInfo Describes the LiveSync operation - for which project directory is the operation and other settings. + * @return {Promise} + */ + finalizeSync(liveSyncInfo: ILiveSyncResultInfo, projectData: IProjectData): Promise; } interface IAndroidLivesyncTool { diff --git a/lib/services/livesync/android-device-livesync-sockets-service.ts b/lib/services/livesync/android-device-livesync-sockets-service.ts index 5bb4048c2f..e2d172fda0 100644 --- a/lib/services/livesync/android-device-livesync-sockets-service.ts +++ b/lib/services/livesync/android-device-livesync-sockets-service.ts @@ -39,18 +39,23 @@ export class AndroidDeviceSocketsLiveSyncService extends DeviceLiveSyncServiceBa return `${LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${appIdentifier}-livesync-in-progress`; } - public async finalizeSync(liveSyncInfo: ILiveSyncResultInfo) { - await this.doSync(liveSyncInfo); + public async finalizeSync(liveSyncInfo: ILiveSyncResultInfo, projectData: IProjectData): Promise { + try { + const result = await this.doSync(liveSyncInfo, projectData); + return result; + } finally { + this.livesyncTool.end(); + } } - private async doSync(liveSyncInfo: ILiveSyncResultInfo, { doRefresh = false }: { doRefresh?: boolean } = {}): Promise { + private async doSync(liveSyncInfo: ILiveSyncResultInfo, projectData: IProjectData): Promise { const operationId = this.livesyncTool.generateOperationIdentifier(); let result = { operationId, didRefresh: true }; if (liveSyncInfo.modifiedFilesData.length) { - - const doSyncPromise = this.livesyncTool.sendDoSyncOperation(doRefresh, null, operationId); + const canExecuteFastSync = !liveSyncInfo.isFullSync && this.canExecuteFastSyncForPaths(liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform); + const doSyncPromise = this.livesyncTool.sendDoSyncOperation(canExecuteFastSync, null, operationId); const syncInterval: NodeJS.Timer = setInterval(() => { if (this.livesyncTool.isOperationInProgress(operationId)) { @@ -64,30 +69,29 @@ export class AndroidDeviceSocketsLiveSyncService extends DeviceLiveSyncServiceBa }; this.$processService.attachToProcessExitSignals(this, actionOnEnd); - doSyncPromise.then(actionOnEnd, actionOnEnd); + // We need to clear resources when the action fails + // But we also need the real result of the action. + await doSyncPromise.then(actionOnEnd.bind(this), actionOnEnd.bind(this)); result = await doSyncPromise; + } else { + await this.device.fileSystem.deleteFile(this.getPathToLiveSyncFileOnDevice(liveSyncInfo.deviceAppData.appIdentifier), liveSyncInfo.deviceAppData.appIdentifier); } - await this.device.fileSystem.deleteFile(this.getPathToLiveSyncFileOnDevice(liveSyncInfo.deviceAppData.appIdentifier), liveSyncInfo.deviceAppData.appIdentifier); - return result; } - public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo) { + public async refreshApplication(projectData: IProjectData, liveSyncInfo: IAndroidLiveSyncResultInfo) { const canExecuteFastSync = !liveSyncInfo.isFullSync && this.canExecuteFastSyncForPaths(liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform); - - const syncOperationResult = await this.doSync(liveSyncInfo, { doRefresh: canExecuteFastSync }); - - this.livesyncTool.end(); - - if (!canExecuteFastSync || !syncOperationResult.didRefresh) { + if (!canExecuteFastSync || !liveSyncInfo.didRefresh) { await this.device.applicationManager.restartApplication({ appId: liveSyncInfo.deviceAppData.appIdentifier, projectName: projectData.projectName }); } } public async removeFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string): Promise { await this.livesyncTool.removeFiles(_.map(localToDevicePaths, (element: any) => element.filePath)); + + await this.getDeviceHashService(deviceAppData.appIdentifier).removeHashes(localToDevicePaths); } public async transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): Promise { @@ -96,15 +100,21 @@ export class AndroidDeviceSocketsLiveSyncService extends DeviceLiveSyncServiceBa if (isFullSync) { transferredFiles = await this._transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath); } else { - transferredFiles = await this._transferFiles(localToDevicePaths); + transferredFiles = await this._transferFiles(deviceAppData, localToDevicePaths); } return transferredFiles; } - private async _transferFiles(localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + private async _transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { await this.livesyncTool.sendFiles(localToDevicePaths.map(localToDevicePathData => localToDevicePathData.getLocalPath())); + // Update hashes + const deviceHashService = this.getDeviceHashService(deviceAppData.appIdentifier); + if (! await deviceHashService.updateHashes(localToDevicePaths)) { + this.$logger.trace("Unable to find hash file on device. The next livesync command will create it."); + } + return localToDevicePaths; } diff --git a/lib/services/livesync/android-livesync-service.ts b/lib/services/livesync/android-livesync-service.ts index 481b2af57e..9821e7e23e 100644 --- a/lib/services/livesync/android-livesync-service.ts +++ b/lib/services/livesync/android-livesync-service.ts @@ -23,6 +23,25 @@ export class AndroidLiveSyncService extends PlatformLiveSyncServiceBase implemen return this.$injector.resolve(AndroidDeviceLiveSyncService, { device, data }); } + public async liveSyncWatchAction(device: Mobile.IDevice, liveSyncInfo: ILiveSyncWatchInfo): Promise { + const liveSyncResult = await super.liveSyncWatchAction(device, liveSyncInfo); + const result = await this.finalizeSync(device, liveSyncInfo.projectData, liveSyncResult); + return result; + } + + public async fullSync(syncInfo: IFullSyncInfo): Promise { + const liveSyncResult = await super.fullSync(syncInfo); + const result = await this.finalizeSync(syncInfo.device, syncInfo.projectData, liveSyncResult); + return result; + } + public async prepareForLiveSync(device: Mobile.IDevice, data: IProjectDir): Promise { /* */ } + + private async finalizeSync(device: Mobile.IDevice, projectData: IProjectData, liveSyncResult: ILiveSyncResultInfo): Promise { + const liveSyncService = this.getDeviceLiveSyncService(device, projectData); + const finalizeResult = await liveSyncService.finalizeSync(liveSyncResult, projectData); + const result = _.extend(liveSyncResult, finalizeResult); + return result; + } } $injector.register("androidLiveSyncService", AndroidLiveSyncService); diff --git a/lib/services/livesync/android-livesync-tool.ts b/lib/services/livesync/android-livesync-tool.ts index 23275c87d3..0f319faacb 100644 --- a/lib/services/livesync/android-livesync-tool.ts +++ b/lib/services/livesync/android-livesync-tool.ts @@ -22,13 +22,13 @@ const DEFAULT_LOCAL_HOST_ADDRESS = "127.0.0.1"; export class AndroidLivesyncTool implements IAndroidLivesyncTool { private operationPromises: IDictionary; private socketError: string | Error; - private socketConnection: IDuplexSocket; + private socketConnection: INetSocket; private configuration: IAndroidLivesyncToolConfiguration; private pendingConnectionData: { connectionTimer?: NodeJS.Timer, socketTimer?: NodeJS.Timer, rejectHandler?: Function, - socket?: IDuplexSocket + socket?: INetSocket } = null; constructor(private $androidProcessService: Mobile.IAndroidProcessService, @@ -173,6 +173,7 @@ export class AndroidLivesyncTool implements IAndroidLivesyncTool { this.cleanState(socketUid); //call end of the connection (close and error callbacks won't be called - listeners removed) socket.end(); + socket.destroy(); //reject all pending sync requests and clear timeouts this.rejectPendingSyncOperations(socketUid, error); } @@ -254,7 +255,7 @@ export class AndroidLivesyncTool implements IAndroidLivesyncTool { }); } - private createSocket(port: number): IDuplexSocket { + private createSocket(port: number): INetSocket { const socket = new net.Socket(); socket.connect(port, this.configuration.localHostAddress); return socket; @@ -280,7 +281,7 @@ export class AndroidLivesyncTool implements IAndroidLivesyncTool { } } - private handleConnection({ socket, data }: { socket: IDuplexSocket, data: NodeBuffer | string }) { + private handleConnection({ socket, data }: { socket: INetSocket, data: NodeBuffer | string }) { this.socketConnection = socket; this.socketConnection.uid = this.generateOperationIdentifier(); @@ -304,7 +305,7 @@ export class AndroidLivesyncTool implements IAndroidLivesyncTool { }); } - private connectEventuallyUntilTimeout(factory: () => IDuplexSocket, timeout: number): Promise<{socket: IDuplexSocket, data: NodeBuffer | string}> { + private connectEventuallyUntilTimeout(factory: () => INetSocket, timeout: number): Promise<{socket: INetSocket, data: NodeBuffer | string}> { return new Promise((resolve, reject) => { let lastKnownError: Error | string, isConnected = false; @@ -312,7 +313,7 @@ export class AndroidLivesyncTool implements IAndroidLivesyncTool { const connectionTimer = setTimeout(() => { if (!isConnected) { isConnected = true; - reject(lastKnownError || { message: "Socket connection timeouted." }); + reject(lastKnownError || new Error("Socket connection timeouted.")); this.pendingConnectionData = null; } }, timeout); diff --git a/lib/services/livesync/device-livesync-service-base.ts b/lib/services/livesync/device-livesync-service-base.ts index 6bf76cbac1..d2e447cecc 100644 --- a/lib/services/livesync/device-livesync-service-base.ts +++ b/lib/services/livesync/device-livesync-service-base.ts @@ -39,7 +39,11 @@ export abstract class DeviceLiveSyncServiceBase { return transferredFiles; } - public async finalizeSync(liveSyncInfo: ILiveSyncResultInfo): Promise { + public async finalizeSync(liveSyncInfo: ILiveSyncResultInfo, projectData: IProjectData): Promise { //implement in case a sync point for all remove/create operation is needed + return { + didRefresh:true, + operationId: "" + }; } } diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index ad17982098..5a6de53cd0 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -164,9 +164,6 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi }; try { - const platformLiveSyncService = this.getLiveSyncService(liveSyncResultInfo.deviceAppData.platform); - const deviceLivesyncService = platformLiveSyncService.getDeviceLiveSyncService(deviceAppData.device, projectData); - await deviceLivesyncService.finalizeSync(liveSyncResultInfo); await deviceAppData.device.applicationManager.stopApplication({ appId: applicationId, projectName: projectData.projectName }); // Now that we've stopped the application we know it isn't started, so set debugOptions.start to false // so that it doesn't default to true in attachDebugger method diff --git a/lib/services/pacote-service.ts b/lib/services/pacote-service.ts index ac4f5d9e99..a69f12481f 100644 --- a/lib/services/pacote-service.ts +++ b/lib/services/pacote-service.ts @@ -29,6 +29,10 @@ export class PacoteService implements IPacoteService { _.extend(extractOptions, options); } + if (this.$fs.exists(packageName)) { + packageName = path.resolve(packageName); + } + const cache = await this.$npm.getCachePath(); return new Promise((resolve, reject) => { const source = pacote.tarball.stream(packageName, { cache }); diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index acea014f6d..8e5addb6b0 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -3,7 +3,7 @@ import * as path from "path"; import * as shelljs from "shelljs"; import { format } from "util"; import { exported } from "../common/decorators"; -import { Hooks } from "../constants"; +import { Hooks, TemplatesV2PackageJsonKeysToRemove } from "../constants"; export class ProjectService implements IProjectService { @@ -212,7 +212,7 @@ export class ProjectService implements IProjectService { const packageJsonData = this.$fs.readJson(projectFilePath); // Remove the metadata keys from the package.json - let updatedPackageJsonData = _.omitBy(packageJsonData, (value: any, key: string) => _.startsWith(key, "_")); + let updatedPackageJsonData = _.omitBy(packageJsonData, (value: any, key: string) => _.startsWith(key, "_") || TemplatesV2PackageJsonKeysToRemove.indexOf(key) !== -1); updatedPackageJsonData = _.merge(updatedPackageJsonData, this.packageJsonDefaultData); if (updatedPackageJsonData.nativescript && updatedPackageJsonData.nativescript.templateVersion) { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 7e677cd8f6..024a1bd252 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -82,6 +82,12 @@ "integrity": "sha1-dqfBnFD+juKxZm1lP/XVV8MP4P8=", "dev": true }, + "@types/lodash": { + "version": "4.14.116", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.116.tgz", + "integrity": "sha512-lRnAtKnxMXcYYXqOiotTmJd74uawNWuPnsnPrrO7HiFuE3npE2iQhfABatbYDyxTNqZNuXzcKGhw37R7RjBFLg==", + "dev": true + }, "@types/node": { "version": "6.0.61", "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.61.tgz", @@ -3684,9 +3690,9 @@ "integrity": "sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k=" }, "lodash": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.13.1.tgz", - "integrity": "sha1-g+SxCRP0hJbU0W/sSlYK8u50S2g=" + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" }, "lodash-node": { "version": "2.4.1", diff --git a/package.json b/package.json index 60b684d4ee..d465dcb385 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "ios-sim-portable": "4.0.2", "jimp": "0.2.28", "lockfile": "1.0.3", - "lodash": "4.13.1", + "lodash": "4.17.10", "log4js": "1.0.1", "marked": "0.3.12", "marked-terminal": "2.0.0", @@ -89,6 +89,7 @@ "@types/chokidar": "1.6.0", "@types/color": "3.0.0", "@types/lockfile": "1.0.0", + "@types/lodash": "4.14.116", "@types/node": "6.0.61", "@types/ora": "1.3.3", "@types/qr-image": "3.2.0",