Skip to content

fix: final Update and Migrate improvements #4829

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 10, 2019
99 changes: 70 additions & 29 deletions lib/controllers/migrate-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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!";
Expand All @@ -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" },
Expand All @@ -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<string> {
Expand All @@ -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);
Expand All @@ -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<boolean> {
const projectData = this.$projectDataService.getProjectData(projectDir);

Expand Down Expand Up @@ -158,6 +150,43 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC
}
}

private async getAngularTypeScriptVersion(projectData: IProjectData): Promise<string> {
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<void> {
this.$logger.info("Clean old project artefacts.");
this.$projectDataService.removeNSConfigProperty(projectData.projectDir, "useLegacyWorkflow");
Expand Down Expand Up @@ -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<string> {
if (!dependency.verifiedVersion && dependency.getVerifiedVersion) {
dependency.verifiedVersion = await dependency.getVerifiedVersion(projectData);
}

return dependency.verifiedVersion;
}

private async shouldMigrateDependencyVersion(dependency: IMigrationDependency, projectData: IProjectData): Promise<boolean> {
Expand All @@ -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<boolean> {
Expand Down
20 changes: 17 additions & 3 deletions lib/controllers/update-controller-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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 });
}
}
25 changes: 8 additions & 17 deletions lib/controllers/update-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<void> {
Expand All @@ -60,7 +59,7 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon
public async shouldUpdate({ projectDir, version }: { projectDir: string, version?: string }): Promise<boolean> {
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);

Expand Down Expand Up @@ -94,7 +93,7 @@ export class UpdateController extends UpdateControllerBase implements IUpdateCon

private async updateProject(projectData: IProjectData, version: string): Promise<void> {
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);

Expand Down Expand Up @@ -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<string>): IDictionary<string> {
return _.pickBy(dependencies, (value, key) => {
return UpdateController.updatableDependencies.indexOf(key) > -1;
Expand Down
2 changes: 1 addition & 1 deletion lib/declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ interface IInfoService {
interface IAndroidResourcesMigrationService {
canMigrate(platformString: string): boolean;
hasMigrated(appResourcesDir: string): boolean;
migrate(appResourcesDir: string): Promise<void>;
migrate(appResourcesDir: string, backupLocation?: string): Promise<void>;
}

/**
Expand Down
1 change: 1 addition & 0 deletions lib/definitions/migrate.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface IMigrationDependency extends IDependency {
replaceWith?: string;
warning?: string;
verifiedVersion?: string;
getVerifiedVersion?: (projectData: IProjectData) => Promise<string>;
shouldAddIfMissing?: boolean;
shouldMigrateAction?: (projectData: IProjectData) => boolean;
migrateAction?: (projectData: IProjectData, migrationBackupDirPath: string) => Promise<IMigrationDependency[]>;
Expand Down
4 changes: 2 additions & 2 deletions lib/services/android-resources-migration-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
public async migrate(appResourcesDir: string, backupLocation?: string): Promise<void> {
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);
Expand Down