diff --git a/lib/controllers/migrate-controller.ts b/lib/controllers/migrate-controller.ts index 6c71445c32..b5fe07f463 100644 --- a/lib/controllers/migrate-controller.ts +++ b/lib/controllers/migrate-controller.ts @@ -12,6 +12,7 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC protected $platformsDataService: IPlatformsDataService, protected $packageInstallationManager: IPackageInstallationManager, protected $packageManager: IPackageManager, + protected $pacoteService: IPacoteService, private $androidResourcesMigrationService: IAndroidResourcesMigrationService, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $logger: ILogger, @@ -20,9 +21,10 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC private $pluginsService: IPluginsService, private $projectDataService: IProjectDataService, private $resources: IResourceLoader) { - super($fs, $platformCommandHelper, $platformsDataService, $packageInstallationManager, $packageManager); + super($fs, $platformCommandHelper, $platformsDataService, $packageInstallationManager, $packageManager, $pacoteService); } + static readonly typescriptPackageName: string = "typescript"; static readonly backupFolder: string = ".migration_backup"; static readonly migrateFailMessage: string = "Could not migrate the project!"; static readonly backupFailMessage: string = "Could not backup project folders!"; @@ -38,15 +40,14 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC ]; private migrationDependencies: IMigrationDependency[] = [ - { packageName: constants.TNS_CORE_MODULES_NAME, verifiedVersion: "6.0.0-rc-2019-07-08-111131-01" }, + { packageName: constants.TNS_CORE_MODULES_NAME, verifiedVersion: "6.0.0-rc-2019-07-09-183845-06" }, { 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: "tns-platform-declarations", isDev: true, verifiedVersion: "6.0.0-rc-2019-07-09-183845-06" }, { packageName: "node-sass", isDev: true, verifiedVersion: "4.12.0" }, - { packageName: "typescript", isDev: true, verifiedVersion: "3.4.1" }, { packageName: "nativescript-dev-sass", isDev: true, replaceWith: "node-sass" }, - { packageName: "nativescript-dev-typescript", isDev: true, replaceWith: "typescript" }, + { packageName: "nativescript-dev-typescript", isDev: true, replaceWith: MigrateController.typescriptPackageName }, { packageName: "nativescript-dev-less", isDev: true, shouldRemove: true, warning: "LESS CSS is not supported out of the box. In order to enable it, follow the steps in this feature request: https://github.com/NativeScript/nativescript-dev-webpack/issues/967" }, - { packageName: constants.WEBPACK_PLUGIN_NAME, isDev: true, shouldAddIfMissing: true, verifiedVersion: "1.0.0-rc-2019-07-08-135456-03" }, + { packageName: constants.WEBPACK_PLUGIN_NAME, isDev: true, shouldAddIfMissing: true, verifiedVersion: "1.0.0-rc-2019-07-10-002255-01" }, { packageName: "nativescript-camera", verifiedVersion: "4.5.0" }, { packageName: "nativescript-geolocation", verifiedVersion: "5.1.0" }, { packageName: "nativescript-imagepicker", verifiedVersion: "6.2.0" }, @@ -59,19 +60,18 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC { packageName: "nativescript-ui-calendar", verifiedVersion: "5.0.0-androidx-110619-2" }, { packageName: "nativescript-ui-autocomplete", verifiedVersion: "5.0.0-androidx-110619" }, { packageName: "nativescript-datetimepicker", verifiedVersion: "1.1.0" }, - //TODO update with compatible with webpack only hooks { packageName: "kinvey-nativescript-sdk", verifiedVersion: "4.2.1" }, - //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.1" }, + { packageName: "nativescript-plugin-firebase", verifiedVersion: "9.0.2" }, + // TODO: update with no prerelease version compatible with webpack only hooks + { packageName: "nativescript-vue", verifiedVersion: "2.3.0-rc.2" }, { packageName: "nativescript-permissions", verifiedVersion: "1.3.0" }, { packageName: "nativescript-cardview", verifiedVersion: "3.2.0" }, { packageName: "nativescript-unit-test-runner", verifiedVersion: "0.6.4", shouldMigrateAction: (projectData: IProjectData) => this.hasDependency({ packageName: "nativescript-unit-test-runner", isDev: false }, projectData), migrateAction: this.migrateUnitTestRunner.bind(this) - } + }, + { packageName: MigrateController.typescriptPackageName, isDev: true, getVerifiedVersion: this.getAngularTypeScriptVersion.bind(this) } ]; get verifiedPlatformVersions(): IDictionary { @@ -96,14 +96,14 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC } try { - this.$logger.info("Backup auto-generated files."); + this.$logger.info("Clean auto-generated files."); this.handleAutoGeneratedFiles(backupDir, projectData); - this.$logger.info("Backup auto-generated files complete."); + this.$logger.info("Clean auto-generated files complete."); } catch (error) { this.$logger.trace(`Error during auto-generated files handling. ${(error && error.message) || error}`); } - await this.migrateOldAndroidAppResources(projectData); + await this.migrateOldAndroidAppResources(projectData, backupDir); try { await this.cleanUpProject(projectData); @@ -114,14 +114,6 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC } } - private async migrateOldAndroidAppResources(projectData: IProjectData) { - const appResourcesPath = projectData.getAppResourcesDirectoryPath(); - if (!this.$androidResourcesMigrationService.hasMigrated(appResourcesPath)) { - this.$logger.info("Migrate old Android App_Resources structure."); - await this.$androidResourcesMigrationService.migrate(appResourcesPath); - } - } - public async shouldMigrate({ projectDir }: IProjectDir): Promise { const projectData = this.$projectDataService.getProjectData(projectDir); @@ -158,6 +150,43 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC } } + private async getAngularTypeScriptVersion(projectData: IProjectData): Promise { + let verifiedVersion = "3.4.1"; + try { + const ngcPackageName = "@angular/compiler-cli"; + // e.g. ~8.0.0 + let ngcVersion = projectData.dependencies[ngcPackageName] || projectData.devDependencies[ngcPackageName]; + if (ngcVersion) { + // e.g. 8.0.3 + ngcVersion = await this.$packageInstallationManager.maxSatisfyingVersion(ngcPackageName, ngcVersion); + const ngcManifest = await this.getPackageManifest(ngcPackageName, ngcVersion); + // e.g. >=3.4 <3.5 + verifiedVersion = (ngcManifest && ngcManifest.peerDependencies && + ngcManifest.peerDependencies[MigrateController.typescriptPackageName]) || verifiedVersion; + + // e.g. 3.4.4 + verifiedVersion = await this.$packageInstallationManager.maxSatisfyingVersion( + MigrateController.typescriptPackageName, verifiedVersion); + } + } catch (error) { + this.$logger.warn(`Unable to determine the TypeScript version based on the Angular packages. Error is: '${error}'.`); + } + + return verifiedVersion; + } + + private async migrateOldAndroidAppResources(projectData: IProjectData, backupDir: string) { + const appResourcesPath = projectData.getAppResourcesDirectoryPath(); + if (!this.$androidResourcesMigrationService.hasMigrated(appResourcesPath)) { + this.$logger.info("Migrate old Android App_Resources structure."); + try { + await this.$androidResourcesMigrationService.migrate(appResourcesPath, backupDir); + } catch (error) { + this.$logger.warn("Migrate old Android App_Resources structure failed: ", error.message); + } + } + } + private async cleanUpProject(projectData: IProjectData): Promise { this.$logger.info("Clean old project artefacts."); this.$projectDataService.removeNSConfigProperty(projectData.projectDir, "useLegacyWorkflow"); @@ -270,23 +299,34 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC if (!replacementDep) { this.$errors.failWithoutHelp("Failed to find replacement dependency."); } + + const replacementDepVersion = await this.getDependencyVerifiedVersion(replacementDep, projectData); this.$logger.info(`Replacing '${dependency.packageName}' with '${replacementDep.packageName}'.`); - this.$pluginsService.addToPackageJson(replacementDep.packageName, replacementDep.verifiedVersion, replacementDep.isDev, projectData.projectDir); + this.$pluginsService.addToPackageJson(replacementDep.packageName, replacementDepVersion, replacementDep.isDev, projectData.projectDir); } return; } + const dependencyVersion = await this.getDependencyVerifiedVersion(dependency, projectData); if (hasDependency && await this.shouldMigrateDependencyVersion(dependency, projectData)) { - this.$logger.info(`Updating '${dependency.packageName}' to compatible version '${dependency.verifiedVersion}'`); - this.$pluginsService.addToPackageJson(dependency.packageName, dependency.verifiedVersion, dependency.isDev, projectData.projectDir); + this.$logger.info(`Updating '${dependency.packageName}' to compatible version '${dependencyVersion}'`); + this.$pluginsService.addToPackageJson(dependency.packageName, dependencyVersion, dependency.isDev, projectData.projectDir); return; } if (!hasDependency && dependency.shouldAddIfMissing) { - this.$logger.info(`Adding '${dependency.packageName}' with version '${dependency.verifiedVersion}'`); - this.$pluginsService.addToPackageJson(dependency.packageName, dependency.verifiedVersion, dependency.isDev, projectData.projectDir); + this.$logger.info(`Adding '${dependency.packageName}' with version '${dependencyVersion}'`); + this.$pluginsService.addToPackageJson(dependency.packageName, dependencyVersion, dependency.isDev, projectData.projectDir); + } + } + + private async getDependencyVerifiedVersion(dependency: IMigrationDependency, projectData: IProjectData): Promise { + if (!dependency.verifiedVersion && dependency.getVerifiedVersion) { + dependency.verifiedVersion = await dependency.getVerifiedVersion(projectData); } + + return dependency.verifiedVersion; } private async shouldMigrateDependencyVersion(dependency: IMigrationDependency, projectData: IProjectData): Promise { @@ -295,8 +335,9 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC const packageName = dependency.packageName; const version = dependencies[packageName] || devDependencies[packageName]; const maxSatisfyingVersion = await this.getMaxDependencyVersion(dependency.packageName, version); + const dependencyVersion = await this.getDependencyVerifiedVersion(dependency, projectData); - return !(maxSatisfyingVersion && semver.gte(maxSatisfyingVersion, dependency.verifiedVersion)); + return !(maxSatisfyingVersion && semver.gte(maxSatisfyingVersion, dependencyVersion)); } protected async shouldUpdateRuntimeVersion({ targetVersion, platform, projectData }: { targetVersion: string, platform: string, projectData: IProjectData }): Promise { diff --git a/lib/controllers/update-controller-base.ts b/lib/controllers/update-controller-base.ts index ea0c591d39..54cbc3ab7e 100644 --- a/lib/controllers/update-controller-base.ts +++ b/lib/controllers/update-controller-base.ts @@ -2,11 +2,17 @@ import * as path from "path"; import * as semver from "semver"; export class UpdateControllerBase { + protected getPackageManifest: Function; + constructor(protected $fs: IFileSystem, protected $platformCommandHelper: IPlatformCommandHelper, protected $platformsDataService: IPlatformsDataService, protected $packageInstallationManager: IPackageInstallationManager, - protected $packageManager: IPackageManager) { + protected $packageManager: IPackageManager, + protected $pacoteService: IPacoteService) { + this.getPackageManifest = _.memoize(this._getManifestManifest, (...args) => { + return args.join("@"); + }); } protected restoreBackup(folders: string[], backupDir: string, projectDir: string): void { @@ -39,13 +45,13 @@ export class UpdateControllerBase { return (dependencies && dependencies[dependency.packageName]) || (devDependencies && devDependencies[dependency.packageName]); } - protected hasRuntimeDependency({platform, projectData}: {platform: string, projectData: IProjectData}): boolean { + protected hasRuntimeDependency({ platform, projectData }: { platform: string, projectData: IProjectData }): boolean { const lowercasePlatform = platform.toLowerCase(); const currentPlatformVersion = this.$platformCommandHelper.getCurrentPlatformVersion(lowercasePlatform, projectData); return !!currentPlatformVersion; } - protected async getMaxRuntimeVersion({platform, projectData}: {platform: string, projectData: IProjectData}) { + protected async getMaxRuntimeVersion({ platform, projectData }: { platform: string, projectData: IProjectData }) { const lowercasePlatform = platform.toLowerCase(); const currentPlatformVersion = this.$platformCommandHelper.getCurrentPlatformVersion(lowercasePlatform, projectData); const platformData = this.$platformsDataService.getPlatformData(lowercasePlatform, projectData); @@ -66,4 +72,12 @@ export class UpdateControllerBase { return maxDependencyVersion; } + + private async _getManifestManifest(templateName: string, version?: string) { + const packageVersion = semver.valid(version) || + await this.$packageManager.getTagVersion(templateName, version) || + await this.$packageInstallationManager.getLatestCompatibleVersionSafe(templateName); + + return await this.$pacoteService.manifest(`${templateName}@${packageVersion}`, { fullMetadata: true }); + } } diff --git a/lib/controllers/update-controller.ts b/lib/controllers/update-controller.ts index 8e662ce5b8..82b14d64dd 100644 --- a/lib/controllers/update-controller.ts +++ b/lib/controllers/update-controller.ts @@ -4,8 +4,10 @@ import * as constants from "../constants"; import { UpdateControllerBase } from "./update-controller-base"; export class UpdateController extends UpdateControllerBase implements IUpdateController { - private getTemplateManifest: Function; - static readonly updatableDependencies: string[] = [constants.TNS_CORE_MODULES_NAME, constants.TNS_CORE_MODULES_WIDGETS_NAME]; + static readonly updatableDependencies: string[] = [ + constants.TNS_CORE_MODULES_NAME, + constants.TNS_CORE_MODULES_WIDGETS_NAME, + constants.WEBPACK_PLUGIN_NAME]; static readonly folders: string[] = [ constants.LIB_DIR_NAME, constants.HOOKS_DIR_NAME, @@ -28,12 +30,9 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon private $addPlatformService: IAddPlatformService, private $logger: ILogger, private $pluginsService: IPluginsService, - private $pacoteService: IPacoteService, + protected $pacoteService: IPacoteService, private $projectDataService: IProjectDataService) { - super($fs, $platformCommandHelper, $platformsDataService, $packageInstallationManager, $packageManager); - this.getTemplateManifest = _.memoize(this._getTemplateManifest, (...args) => { - return args.join("@"); - }); + super($fs, $platformCommandHelper, $platformsDataService, $packageInstallationManager, $packageManager, $pacoteService); } public async update(updateOptions: IUpdateOptions): Promise { @@ -60,7 +59,7 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon public async shouldUpdate({ projectDir, version }: { projectDir: string, version?: string }): Promise { const projectData = this.$projectDataService.getProjectData(projectDir); const templateName = this.getTemplateName(projectData); - const templateManifest = await this.getTemplateManifest(templateName, version); + const templateManifest = await this.getPackageManifest(templateName, version); const dependencies = this.getUpdatableDependencies(templateManifest.dependencies); const devDependencies = this.getUpdatableDependencies(templateManifest.devDependencies); @@ -94,7 +93,7 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon private async updateProject(projectData: IProjectData, version: string): Promise { const templateName = this.getTemplateName(projectData); - const templateManifest = await this.getTemplateManifest(templateName, version); + const templateManifest = await this.getPackageManifest(templateName, version); const dependencies = this.getUpdatableDependencies(templateManifest.dependencies); const devDependencies = this.getUpdatableDependencies(templateManifest.devDependencies); @@ -180,14 +179,6 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon return maxTemplateRuntimeVersion && maxRuntimeVersion && semver.gt(maxTemplateRuntimeVersion, maxRuntimeVersion); } - private async _getTemplateManifest(templateName: string, version?: string) { - const packageVersion = semver.valid(version) || - await this.$packageManager.getTagVersion(templateName, version) || - await this.$packageInstallationManager.getLatestCompatibleVersionSafe(templateName); - - return await this.$pacoteService.manifest(`${templateName}@${packageVersion}`, { fullMetadata: true }); - } - private getUpdatableDependencies(dependencies: IDictionary): IDictionary { return _.pickBy(dependencies, (value, key) => { return UpdateController.updatableDependencies.indexOf(key) > -1; diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index eed7ccdcf7..b1b2c43a64 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -603,7 +603,7 @@ interface IInfoService { interface IAndroidResourcesMigrationService { canMigrate(platformString: string): boolean; hasMigrated(appResourcesDir: string): boolean; - migrate(appResourcesDir: string): Promise; + migrate(appResourcesDir: string, backupLocation?: string): Promise; } /** diff --git a/lib/definitions/migrate.d.ts b/lib/definitions/migrate.d.ts index fca836ae31..e7e434693d 100644 --- a/lib/definitions/migrate.d.ts +++ b/lib/definitions/migrate.d.ts @@ -13,6 +13,7 @@ interface IMigrationDependency extends IDependency { replaceWith?: string; warning?: string; verifiedVersion?: string; + getVerifiedVersion?: (projectData: IProjectData) => Promise; shouldAddIfMissing?: boolean; shouldMigrateAction?: (projectData: IProjectData) => boolean; migrateAction?: (projectData: IProjectData, migrationBackupDirPath: string) => Promise; diff --git a/lib/services/android-resources-migration-service.ts b/lib/services/android-resources-migration-service.ts index a565bacc22..6167154e53 100644 --- a/lib/services/android-resources-migration-service.ts +++ b/lib/services/android-resources-migration-service.ts @@ -20,10 +20,10 @@ export class AndroidResourcesMigrationService implements IAndroidResourcesMigrat return this.$fs.exists(path.join(appResourcesDir, AndroidResourcesMigrationService.ANDROID_DIR, constants.SRC_DIR, constants.MAIN_DIR)); } - public async migrate(appResourcesDir: string): Promise { + public async migrate(appResourcesDir: string, backupLocation?: string): Promise { const originalAppResources = path.join(appResourcesDir, AndroidResourcesMigrationService.ANDROID_DIR); const appResourcesDestination = path.join(appResourcesDir, AndroidResourcesMigrationService.ANDROID_DIR_TEMP); - const appResourcesBackup = path.join(appResourcesDir, AndroidResourcesMigrationService.ANDROID_DIR_OLD); + const appResourcesBackup = path.join(backupLocation || appResourcesDir, AndroidResourcesMigrationService.ANDROID_DIR_OLD); try { await this.tryMigrate(originalAppResources, appResourcesDestination, appResourcesBackup);