Skip to content

Improve the migrate command based on the feedback #4816

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 5 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 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,
private $androidResourcesMigrationService: IAndroidResourcesMigrationService,
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $logger: ILogger,
private $errors: IErrors,
Expand Down Expand Up @@ -42,10 +43,9 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC
{ 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: "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-02-161545-02" },
{ packageName: "nativescript-camera", verifiedVersion: "4.5.0" },
{ packageName: "nativescript-geolocation", verifiedVersion: "5.1.0" },
Expand Down Expand Up @@ -103,6 +103,8 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC
this.$logger.trace(`Error during auto-generated files handling. ${(error && error.message) || error}`);
}

await this.migrateOldAndroidAppResources(projectData);

try {
await this.cleanUpProject(projectData);
await this.migrateDependencies(projectData);
Expand All @@ -112,6 +114,14 @@ 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 All @@ -123,7 +133,7 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC
return true;
}

if (hasDependency && dependency.replaceWith) {
if (hasDependency && (dependency.replaceWith || dependency.shouldRemove)) {
return true;
}

Expand All @@ -134,6 +144,10 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC
if (!hasDependency && dependency.shouldAddIfMissing) {
return true;
}

if (!this.$androidResourcesMigrationService.hasMigrated(projectData.getAppResourcesDirectoryPath())) {
return true;
}
}

for (const platform in this.$devicePlatformsConstants) {
Expand All @@ -146,6 +160,7 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC

private async cleanUpProject(projectData: IProjectData): Promise<void> {
this.$logger.info("Clean old project artefacts.");
this.$projectDataService.removeNsConfigProperty(projectData.projectDir, "useLegacyWorkflow");
this.$fs.deleteDirectory(path.join(projectData.projectDir, constants.HOOKS_DIR_NAME));
this.$fs.deleteDirectory(path.join(projectData.projectDir, constants.PLATFORMS_DIR_NAME));
this.$fs.deleteDirectory(path.join(projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME));
Expand Down Expand Up @@ -244,15 +259,21 @@ export class MigrateController extends UpdateControllerBase implements IMigrateC

private async migrateDependency(dependency: IMigrationDependency, projectData: IProjectData): Promise<void> {
const hasDependency = this.hasDependency(dependency, projectData);
if (dependency.warning) {
this.$logger.warn(dependency.warning);
}

if (hasDependency && dependency.replaceWith) {
if (hasDependency && (dependency.replaceWith || dependency.shouldRemove)) {
this.$pluginsService.removeFromPackageJson(dependency.packageName, projectData.projectDir);
const replacementDep = _.find(this.migrationDependencies, migrationPackage => migrationPackage.packageName === dependency.replaceWith);
if (!replacementDep) {
this.$errors.failWithoutHelp("Failed to find replacement dependency.");
if (dependency.replaceWith) {
const replacementDep = _.find(this.migrationDependencies, migrationPackage => migrationPackage.packageName === dependency.replaceWith);
if (!replacementDep) {
this.$errors.failWithoutHelp("Failed to find replacement dependency.");
}
this.$logger.info(`Replacing '${dependency.packageName}' with '${replacementDep.packageName}'.`);
this.$pluginsService.addToPackageJson(replacementDep.packageName, replacementDep.verifiedVersion, replacementDep.isDev, projectData.projectDir);
}
this.$logger.info(`Replacing '${dependency.packageName}' with '${replacementDep.packageName}'.`);
this.$pluginsService.addToPackageJson(replacementDep.packageName, replacementDep.verifiedVersion, replacementDep.isDev, projectData.projectDir);

return;
}

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 @@ -11,6 +11,7 @@ interface IDependency {
interface IMigrationDependency extends IDependency {
shouldRemove?: boolean;
replaceWith?: string;
warning?: string;
verifiedVersion?: string;
shouldAddIfMissing?: boolean;
shouldMigrateAction?: (projectData: IProjectData) => boolean;
Expand Down
28 changes: 15 additions & 13 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ interface INsConfig {
appPath?: string;
appResourcesPath?: string;
shared?: boolean;
useLegacyWorkflow?: boolean;
previewAppSchema?: string;
}

Expand All @@ -101,11 +100,6 @@ interface IProjectData extends ICreateProjectData {
*/
isShared: boolean;

/**
* Defines if the project has hmr enabled by default
*/
useLegacyWorkflow: boolean;

/**
* Defines the schema for the preview app
*/
Expand Down Expand Up @@ -150,6 +144,14 @@ interface IProjectDataService {
*/
removeNSProperty(projectDir: string, propertyName: string): void;

/**
* Removes a property from `nsconfig.json`.
* @param {string} projectDir The project directory - the place where the `nsconfig.json` is located.
* @param {string} propertyName The name of the property to be removed.
* @returns {void}
*/
removeNsConfigProperty(projectDir: string, propertyName: string): void;

/**
* Removes dependency from package.json
* @param {string} projectDir The project directory - the place where the root package.json is located.
Expand Down Expand Up @@ -192,12 +194,12 @@ interface IProjectDataService {
*/
getAppExecutableFiles(projectDir: string): string[];

/**
* Returns a value from `nativescript` key in project's package.json.
* @param {string} jsonData The project directory - the place where the root package.json is located.
* @param {string} propertyName The name of the property to be checked in `nativescript` key.
* @returns {any} The value of the property.
*/
/**
* Returns a value from `nativescript` key in project's package.json.
* @param {string} jsonData The project directory - the place where the root package.json is located.
* @param {string} propertyName The name of the property to be checked in `nativescript` key.
* @returns {any} The value of the property.
*/
getNSValueFromContent(jsonData: Object, propertyName: string): any;
}

Expand Down Expand Up @@ -496,7 +498,7 @@ interface IRemoveExtensionsOptions {
pbxProjPath: string
}

interface IRemoveWatchAppOptions extends IRemoveExtensionsOptions{}
interface IRemoveWatchAppOptions extends IRemoveExtensionsOptions { }

interface IRubyFunction {
functionName: string;
Expand Down
2 changes: 0 additions & 2 deletions lib/project-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ export class ProjectData implements IProjectData {
public buildXcconfigPath: string;
public podfilePath: string;
public isShared: boolean;
public useLegacyWorkflow: boolean;
public previewAppSchema: string;

constructor(private $fs: IFileSystem,
Expand Down Expand Up @@ -137,7 +136,6 @@ export class ProjectData implements IProjectData {
this.buildXcconfigPath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.BUILD_XCCONFIG_FILE_NAME);
this.podfilePath = path.join(this.appResourcesDirectoryPath, this.$devicePlatformsConstants.iOS, constants.PODFILE_NAME);
this.isShared = !!(this.nsConfig && this.nsConfig.shared);
this.useLegacyWorkflow = this.nsConfig && this.nsConfig.useLegacyWorkflow;
this.previewAppSchema = this.nsConfig && this.nsConfig.previewAppSchema;
return;
}
Expand Down
47 changes: 46 additions & 1 deletion lib/services/project-data-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as path from "path";
import { ProjectData } from "../project-data";
import * as constants from "../constants";
import { parseJson } from "../common/helpers";
import { exported } from "../common/decorators";
import {
NATIVESCRIPT_PROPS_INTERNAL_DELIMITER,
Expand Down Expand Up @@ -125,6 +127,12 @@ export class ProjectDataService implements IProjectDataService {
};
}

public removeNsConfigProperty(projectDir: string, propertyName: string): void {
this.$logger.trace(`Removing "${propertyName}" property from nsconfig.`);
this.updateNsConfigValue(projectDir, null, [propertyName]);
this.$logger.trace(`"${propertyName}" property successfully removed.`);
}

@exported("projectDataService")
public async getAndroidAssetsStructure(opts: IProjectDir): Promise<IAssetGroup> {
// TODO: Use image-size package to get the width and height of an image.
Expand Down Expand Up @@ -180,6 +188,43 @@ export class ProjectDataService implements IProjectDataService {

return files;
}
private refreshProjectData(projectDir: string) {
if (this.projectDataCache[projectDir]) {
this.projectDataCache[projectDir].initializeProjectData(projectDir);
}
}

private updateNsConfigValue(projectDir: string, updateObject?: INsConfig, propertiesToRemove?: string[]): void {
const nsConfigPath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME);
const currentNsConfig = this.getNsConfig(nsConfigPath);
let newNsConfig = currentNsConfig;
if (updateObject) {
newNsConfig = _.assign(newNsConfig || this.getNsConfigDefaultObject(), updateObject);
}

if (newNsConfig && propertiesToRemove && propertiesToRemove.length) {
newNsConfig = _.omit(newNsConfig, propertiesToRemove);
}

if (newNsConfig) {
this.$fs.writeJson(nsConfigPath, newNsConfig);
this.refreshProjectData(projectDir);
}
}

private getNsConfig(nsConfigPath: string): INsConfig {
let result: INsConfig = null;
if (this.$fs.exists(nsConfigPath)) {
const nsConfigContent = this.$fs.readText(nsConfigPath);
try {
result = <INsConfig>parseJson(nsConfigContent);
} catch (e) {
// invalid nsconfig => null
}
}

return result;
}

private getImageDefinitions(): IImageDefinitionsStructure {
const pathToImageDefinitions = path.join(__dirname, "..", "..", CLI_RESOURCES_DIR_NAME, AssetConstants.assets, AssetConstants.imageDefinitionsFileName);
Expand Down Expand Up @@ -334,7 +379,7 @@ export class ProjectDataService implements IProjectDataService {
}

private getNsConfigDefaultObject(data?: Object): INsConfig {
const config: INsConfig = { useLegacyWorkflow: false };
const config: INsConfig = {};
Object.assign(config, data);

return config;
Expand Down
67 changes: 0 additions & 67 deletions test/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,73 +263,6 @@ describe("options", () => {
});

describe("setupOptions", () => {
const testCases = [
{
name: "no options are provided",
args: [],
data: [
{ useLegacyWorkflow: undefined, expectedHmr: true, expectedBundle: true },
{ useLegacyWorkflow: false, expectedHmr: true, expectedBundle: true },
{ useLegacyWorkflow: true, expectedHmr: true, expectedBundle: true }
]
},
{
name: " --hmr is provided",
args: ["--hmr"],
data: [
{ useLegacyWorkflow: undefined, expectedHmr: true, expectedBundle: true },
{ useLegacyWorkflow: false, expectedHmr: true, expectedBundle: true },
{ useLegacyWorkflow: true, expectedHmr: true, expectedBundle: true }
]
},
{
name: " --no-hmr is provided",
args: ["--no-hmr"],
data: [
{ useLegacyWorkflow: undefined, expectedHmr: false, expectedBundle: true },
{ useLegacyWorkflow: false, expectedHmr: false, expectedBundle: true },
{ useLegacyWorkflow: true, expectedHmr: false, expectedBundle: true }
]
},
{
name: " --bundle is provided",
args: ["--bundle"],
data: [
{ useLegacyWorkflow: undefined, expectedHmr: true, expectedBundle: true },
{ useLegacyWorkflow: false, expectedHmr: true, expectedBundle: true },
{ useLegacyWorkflow: true, expectedHmr: true, expectedBundle: true }
]
},
{
name: " --release is provided",
args: ["--release"],
data: [
{ useLegacyWorkflow: undefined, expectedHmr: false, expectedBundle: true },
{ useLegacyWorkflow: false, expectedHmr: false, expectedBundle: true },
{ useLegacyWorkflow: true, expectedHmr: false, expectedBundle: true }
]
}
];

_.each([undefined, false, true], useLegacyWorkflow => {
_.each(testCases, testCase => {
it(`should pass correctly when ${testCase.name} and useLegacyWorkflow is ${useLegacyWorkflow}`, () => {
(testCase.args || []).forEach(arg => process.argv.push(arg));

const options: any = createOptions(testInjector);
const projectData = <IProjectData>{ useLegacyWorkflow };
options.setupOptions(projectData);

(testCase.args || []).forEach(arg => process.argv.pop());

const data = testCase.data.find(item => item.useLegacyWorkflow === useLegacyWorkflow);

assert.equal(!!options.argv.hmr, !!data.expectedHmr);
assert.equal(!!options.argv.bundle, !!data.expectedBundle);
});
});
});

const testCasesExpectingToThrow = [
{
name: "--release --hmr",
Expand Down
5 changes: 3 additions & 2 deletions test/stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ export class ProjectDataStub implements IProjectData {
public buildXcconfigPath: string;
public podfilePath: string;
public isShared: boolean;
public useLegacyWorkflow: boolean;
public previewAppSchema: string;

public initializeProjectData(projectDir?: string): void {
Expand Down Expand Up @@ -507,6 +506,8 @@ export class ProjectDataService implements IProjectDataService {

removeNSProperty(propertyName: string): void { }

removeNsConfigProperty(projectDir: string, propertyName: string): void { }

removeDependency(dependencyName: string): void { }

getProjectData(projectDir: string): IProjectData {
Expand All @@ -532,7 +533,7 @@ export class ProjectDataService implements IProjectDataService {
return [];
}

getNSValueFromContent(): any {}
getNSValueFromContent(): any { }
}

export class ProjectHelperStub implements IProjectHelper {
Expand Down