diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 62a35ee7b4..aa7f613e95 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -6,6 +6,7 @@ export class DeployOnDeviceCommand extends ValidatePlatformCommandBase implement public allowedParameters: ICommandParameter[] = []; public dashedOptions = { + watch: { type: OptionType.Boolean, default: false, hasSensitiveValue: false }, hmr: { type: OptionType.Boolean, default: false, hasSensitiveValue: false }, }; diff --git a/lib/common/project-helper.ts b/lib/common/project-helper.ts index 85be0b6be3..e0b34a1f7b 100644 --- a/lib/common/project-helper.ts +++ b/lib/common/project-helper.ts @@ -59,7 +59,7 @@ export class ProjectHelper implements IProjectHelper { if (this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE) { try { const fileContent = this.$fs.readJson(projectFilePath); - const clientSpecificData = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; + const clientSpecificData = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] && fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE].id; return !!clientSpecificData; } catch (err) { this.$errors.failWithoutHelp("The project file is corrupted. Additional technical information: %s", err); diff --git a/lib/controllers/migrate-controller.ts b/lib/controllers/migrate-controller.ts index 6d7f500bc2..67da41c784 100644 --- a/lib/controllers/migrate-controller.ts +++ b/lib/controllers/migrate-controller.ts @@ -37,16 +37,16 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC ]; private migrationDependencies: IMigrationDependency[] = [ - { packageName: constants.TNS_CORE_MODULES_NAME, verifiedVersion: "6.0.0-next-2019-06-20-155941-01" }, - { packageName: constants.TNS_CORE_MODULES_WIDGETS_NAME, verifiedVersion: "6.0.0-next-2019-06-20-155941-01" }, - { packageName: "tns-platform-declarations", verifiedVersion: "6.0.0-next-2019-06-27-082418-01" }, + { packageName: constants.TNS_CORE_MODULES_NAME, verifiedVersion: "6.0.0-rc-2019-06-28-175837-02" }, + { packageName: constants.TNS_CORE_MODULES_WIDGETS_NAME, verifiedVersion: "6.0.0" }, + { packageName: "tns-platform-declarations", isDev: true, verifiedVersion: "6.0.0-rc-2019-06-28-175837-02" }, { packageName: "node-sass", isDev: true, verifiedVersion: "4.12.0" }, { packageName: "typescript", isDev: true, verifiedVersion: "3.4.1" }, { packageName: "less", isDev: true, verifiedVersion: "3.9.0" }, { packageName: "nativescript-dev-sass", isDev: true, replaceWith: "node-sass" }, { packageName: "nativescript-dev-typescript", isDev: true, replaceWith: "typescript" }, { packageName: "nativescript-dev-less", isDev: true, replaceWith: "less" }, - { packageName: constants.WEBPACK_PLUGIN_NAME, isDev: true, shouldAddIfMissing: true, verifiedVersion: "0.25.0-next-2019-06-21-150426-03" }, + { packageName: constants.WEBPACK_PLUGIN_NAME, isDev: true, shouldAddIfMissing: true, verifiedVersion: "1.0.0-rc-2019-07-02-161545-02" }, { packageName: "nativescript-camera", verifiedVersion: "4.5.0" }, { packageName: "nativescript-geolocation", verifiedVersion: "5.1.0" }, { packageName: "nativescript-imagepicker", verifiedVersion: "6.2.0" }, @@ -64,7 +64,7 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC //TODO update with compatible with webpack only hooks { packageName: "nativescript-plugin-firebase", verifiedVersion: "9.0.1" }, //TODO update with no prerelease version compatible with webpack only hooks - { packageName: "nativescript-vue", verifiedVersion: "2.3.0-rc.0" }, + { packageName: "nativescript-vue", verifiedVersion: "2.3.0-rc.1" }, { packageName: "nativescript-permissions", verifiedVersion: "1.3.0" }, { packageName: "nativescript-cardview", verifiedVersion: "3.2.0" }, { @@ -76,8 +76,8 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC get verifiedPlatformVersions(): IDictionary { return { - [this.$devicePlatformsConstants.Android.toLowerCase()]: "6.0.0-2019-06-11-172137-01", - [this.$devicePlatformsConstants.iOS.toLowerCase()]: "6.0.0-2019-06-10-154118-03" + [this.$devicePlatformsConstants.Android.toLowerCase()]: "6.0.0-rc-2019-06-27-172817-03", + [this.$devicePlatformsConstants.iOS.toLowerCase()]: "6.0.0-rc-2019-06-28-105002-01" }; } @@ -165,7 +165,7 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC matchBase: true, nodir: true, absolute: false, - root: projectData.appDirectoryPath + cwd: projectData.appDirectoryPath }; const jsFiles = glob.sync("*.@(js|ts|js.map)", globOptions); @@ -176,7 +176,7 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC const allGeneratedFiles = autoGeneratedJsFiles.concat(autoGeneratedJsMapFiles).concat(autoGeneratedCssFiles); for (const generatedFile of allGeneratedFiles) { - const sourceFile = path.join(projectData.projectDir, generatedFile); + const sourceFile = path.join(projectData.appDirectoryPath, generatedFile); const destinationFile = path.join(backupDir, generatedFile); const destinationFileDir = path.dirname(destinationFile); this.$fs.ensureDirectoryExists(destinationFileDir); diff --git a/lib/controllers/prepare-controller.ts b/lib/controllers/prepare-controller.ts index 52c1eec584..29b9be2b2a 100644 --- a/lib/controllers/prepare-controller.ts +++ b/lib/controllers/prepare-controller.ts @@ -80,24 +80,24 @@ export class PrepareController extends EventEmitter { nativeFilesWatcher: null, webpackCompilerProcess: null }; - } + await this.startJSWatcherWithPrepare(platformData, projectData, prepareData); // -> start watcher + initial compilation + const hasNativeChanges = await this.startNativeWatcherWithPrepare(platformData, projectData, prepareData); // -> start watcher + initial prepare + const result = { platform: platformData.platformNameLowerCase, hasNativeChanges }; - await this.startJSWatcherWithPrepare(platformData, projectData, prepareData); // -> start watcher + initial compilation - const hasNativeChanges = await this.startNativeWatcherWithPrepare(platformData, projectData, prepareData); // -> start watcher + initial prepare + const hasPersistedDataWithNativeChanges = this.persistedData.find(data => data.platform === result.platform && data.hasNativeChanges); + if (hasPersistedDataWithNativeChanges) { + result.hasNativeChanges = true; + } - const result = { platform: platformData.platformNameLowerCase, hasNativeChanges }; - const hasPersistedDataWithNativeChanges = this.persistedData.find(data => data.platform === result.platform && data.hasNativeChanges); - if (hasPersistedDataWithNativeChanges) { - result.hasNativeChanges = true; - } + // TODO: Do not persist this in `this` context. Also it should be per platform. + this.isInitialPrepareReady = true; - this.isInitialPrepareReady = true; + if (this.persistedData && this.persistedData.length) { + this.emitPrepareEvent({ files: [], hasOnlyHotUpdateFiles: false, hasNativeChanges: result.hasNativeChanges, hmrData: null, platform: platformData.platformNameLowerCase }); + } - if (this.persistedData && this.persistedData.length) { - this.emitPrepareEvent({ files: [], hasOnlyHotUpdateFiles: false, hasNativeChanges: result.hasNativeChanges, hmrData: null, platform: platformData.platformNameLowerCase }); + return result; } - - return result; } private async startJSWatcherWithPrepare(platformData: IPlatformData, projectData: IProjectData, prepareData: IPrepareData): Promise { @@ -149,15 +149,19 @@ export class PrepareController extends EventEmitter { @hook('watchPatterns') public async getWatcherPatterns(platformData: IPlatformData, projectData: IProjectData): Promise { - const pluginsNativeDirectories = this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir) - .filter(dep => dep.nativescript) + const dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir) + .filter(dep => dep.nativescript); + const pluginsNativeDirectories = dependencies .map(dep => path.join(dep.directory, PLATFORMS_DIR_NAME, platformData.platformNameLowerCase)); + const pluginsPackageJsonFiles = dependencies.map(dep => path.join(dep.directory, PACKAGE_JSON_FILE_NAME)); const patterns = [ path.join(projectData.projectDir, PACKAGE_JSON_FILE_NAME), path.join(projectData.getAppDirectoryPath(), PACKAGE_JSON_FILE_NAME), path.join(projectData.getAppResourcesRelativeDirectoryPath(), platformData.normalizedPlatformName), - ].concat(pluginsNativeDirectories); + ] + .concat(pluginsNativeDirectories) + .concat(pluginsPackageJsonFiles); return patterns; } diff --git a/lib/controllers/preview-app-controller.ts b/lib/controllers/preview-app-controller.ts index 9cf26564d8..c1883a76bf 100644 --- a/lib/controllers/preview-app-controller.ts +++ b/lib/controllers/preview-app-controller.ts @@ -9,7 +9,8 @@ import { PrepareDataService } from "../services/prepare-data-service"; import { PreviewAppLiveSyncEvents } from "../services/livesync/playground/preview-app-constants"; export class PreviewAppController extends EventEmitter implements IPreviewAppController { - private deviceInitializationPromise: IDictionary> = {}; + private deviceInitializationPromise: IDictionary = {}; + private platformPrepareHandlers: IDictionary = {}; private promise = Promise.resolve(); constructor( @@ -49,9 +50,14 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon } if (this.deviceInitializationPromise[device.id]) { - return this.deviceInitializationPromise[device.id]; + // In some cases devices are reported several times during initialization. + // In case we are already preparing the sending of initial files, disregard consecutive requests for initial files + // until we send the files we are currently preparing. + return null; } + this.deviceInitializationPromise[device.id] = true; + if (device.uniqueId) { await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: TrackActionNames.PreviewAppData, @@ -68,20 +74,25 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon await this.$previewAppPluginsService.comparePluginsOnDevice(data, device); - this.$prepareController.on(PREPARE_READY_EVENT_NAME, async currentPrepareData => { - await this.handlePrepareReadyEvent(data, currentPrepareData.hmrData, currentPrepareData.files, device.platform); - }); + if (!this.platformPrepareHandlers[device.platform]) { + // TODO: Unset this property once the preview operation for this platform is stopped + this.platformPrepareHandlers[device.platform] = true; + + // TODO: Remove the handler once the preview operation for this platform is stopped + this.$prepareController.on(PREPARE_READY_EVENT_NAME, async currentPrepareData => { + await this.handlePrepareReadyEvent(data, currentPrepareData.hmrData, currentPrepareData.files, device.platform); + }); - if (!data.env) { data.env = { }; } + } + + data.env = data.env || {}; data.env.externals = this.$previewAppPluginsService.getExternalPlugins(device); - const prepareData = this.$prepareDataService.getPrepareData(data.projectDir, device.platform.toLowerCase(), { ...data, nativePrepare: { skipNativePrepare: true }, watch: true }); + const prepareData = this.$prepareDataService.getPrepareData(data.projectDir, device.platform.toLowerCase(), { ...data, nativePrepare: { skipNativePrepare: true }, watch: true }); await this.$prepareController.prepare(prepareData); - this.deviceInitializationPromise[device.id] = this.getInitialFilesForPlatformSafe(data, device.platform); - try { - const payloads = await this.deviceInitializationPromise[device.id]; + const payloads = await this.getInitialFilesForPlatformSafe(data, device.platform); return payloads; } finally { this.deviceInitializationPromise[device.id] = null; @@ -116,7 +127,7 @@ export class PreviewAppController extends EventEmitter implements IPreviewAppCon if (status === HmrConstants.HMR_ERROR_STATUS) { const originalUseHotModuleReload = data.useHotModuleReload; data.useHotModuleReload = false; - await this.syncFilesForPlatformSafe(data, { filesToSync: platformHmrData.fallbackFiles }, platform, previewDevice.id ); + await this.syncFilesForPlatformSafe(data, { filesToSync: platformHmrData.fallbackFiles }, platform, previewDevice.id); data.useHotModuleReload = originalUseHotModuleReload; } })); diff --git a/lib/controllers/run-controller.ts b/lib/controllers/run-controller.ts index 290b9505d6..1237555a06 100644 --- a/lib/controllers/run-controller.ts +++ b/lib/controllers/run-controller.ts @@ -62,7 +62,7 @@ export class RunController extends EventEmitter implements IRunController { // so we cannot await it as this will cause infinite loop. const shouldAwaitPendingOperation = !stopOptions || stopOptions.shouldAwaitAllActions; - const deviceIdentifiersToRemove = deviceIdentifiers || _.map(liveSyncProcessInfo.deviceDescriptors, d => d.identifier); + const deviceIdentifiersToRemove = (deviceIdentifiers && deviceIdentifiers.length) ? deviceIdentifiers : _.map(liveSyncProcessInfo.deviceDescriptors, d => d.identifier); const removedDeviceIdentifiers = _.remove(liveSyncProcessInfo.deviceDescriptors, descriptor => _.includes(deviceIdentifiersToRemove, descriptor.identifier)) .map(descriptor => descriptor.identifier); diff --git a/lib/services/livesync/playground/preview-qr-code-service.ts b/lib/services/livesync/playground/preview-qr-code-service.ts index 3da28180c1..2609a2cd55 100644 --- a/lib/services/livesync/playground/preview-qr-code-service.ts +++ b/lib/services/livesync/playground/preview-qr-code-service.ts @@ -44,8 +44,6 @@ export class PreviewQrCodeService implements IPreviewQrCodeService { const qrCodeUrl = this.$previewSdkService.getQrCodeUrl(options); const url = await this.getShortenUrl(qrCodeUrl); - this.$logger.info("======== qrCodeUrl ======== ", qrCodeUrl); - this.$logger.info(); const message = `${EOL} Generating qrcode for url ${url}.`; this.$logger.trace(message); diff --git a/lib/services/webpack/webpack-compiler-service.ts b/lib/services/webpack/webpack-compiler-service.ts index d238bcc7a8..f795aaa72d 100644 --- a/lib/services/webpack/webpack-compiler-service.ts +++ b/lib/services/webpack/webpack-compiler-service.ts @@ -45,7 +45,7 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp let result; if (prepareData.hmr) { - result = this.getUpdatedEmittedFiles(message.emittedFiles, message.webpackRuntimeFiles, message.entryPointFiles); + result = this.getUpdatedEmittedFiles(message.emittedFiles, message.chunkFiles); } else { result = { emittedFiles: message.emittedFiles, fallbackFiles: [], hash: "" }; } @@ -218,16 +218,13 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp return args; } - private getUpdatedEmittedFiles(emittedFiles: string[], webpackRuntimeFiles: string[], entryPointFiles: string[]) { + private getUpdatedEmittedFiles(emittedFiles: string[], chunkFiles: string[]) { let fallbackFiles: string[] = []; let hotHash; let result = emittedFiles.slice(); const hotUpdateScripts = emittedFiles.filter(x => x.endsWith('.hot-update.js')); - if (webpackRuntimeFiles && webpackRuntimeFiles.length) { - result = result.filter(file => webpackRuntimeFiles.indexOf(file) === -1); - } - if (entryPointFiles && entryPointFiles.length) { - result = result.filter(file => entryPointFiles.indexOf(file) === -1); + if (chunkFiles && chunkFiles.length) { + result = result.filter(file => chunkFiles.indexOf(file) === -1); } hotUpdateScripts.forEach(hotUpdateScript => { const { name, hash } = this.parseHotUpdateChunkName(hotUpdateScript); diff --git a/lib/services/webpack/webpack.d.ts b/lib/services/webpack/webpack.d.ts index 7f03b1b3c4..9f1b68e977 100644 --- a/lib/services/webpack/webpack.d.ts +++ b/lib/services/webpack/webpack.d.ts @@ -34,8 +34,7 @@ declare global { interface IWebpackEmitMessage { emittedFiles: string[]; - webpackRuntimeFiles: string[]; - entryPointFiles: string[]; + chunkFiles: string[]; } interface IPlatformProjectService extends NodeJS.EventEmitter, IPlatformProjectServiceBase { diff --git a/test/controllers/run-controller.ts b/test/controllers/run-controller.ts index 9dc2e674da..e33fbc4688 100644 --- a/test/controllers/run-controller.ts +++ b/test/controllers/run-controller.ts @@ -240,6 +240,12 @@ describe("RunController", () => { currentDeviceIdentifiers: ["device1", "device2", "device3"], expectedDeviceIdentifiers: ["device1"], deviceIdentifiersToBeStopped: ["device1", "device4"] + }, + { + name: "stops LiveSync operation for all devices when stop method is called with empty array", + currentDeviceIdentifiers: ["device1", "device2", "device3"], + expectedDeviceIdentifiers: ["device1", "device2", "device3"], + deviceIdentifiersToBeStopped: [] } ];