Skip to content

chore: merge release into master #5097

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
Oct 25, 2019
10 changes: 3 additions & 7 deletions lib/commands/build.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE, AndroidAppBundleMessages, ANDROID_APP_BUNDLE_SIGNING_ERROR_MESSAGE } from "../constants";
import { ANDROID_RELEASE_BUILD_ERROR_MESSAGE, AndroidAppBundleMessages } from "../constants";
import { ValidatePlatformCommandBase } from "./command-base";
import { hasValidAndroidSigning } from "../common/helpers";

Expand Down Expand Up @@ -124,12 +124,8 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand {
this.$androidBundleValidatorHelper.validateRuntimeVersion(this.$projectData);
let canExecute = await super.canExecuteCommandBase(platform, { notConfiguredEnvOptions: { hideSyncToPreviewAppOption: true } });
if (canExecute) {
if ((this.$options.release || this.$options.aab) && !hasValidAndroidSigning(this.$options)) {
if (this.$options.release) {
this.$errors.failWithHelp(ANDROID_RELEASE_BUILD_ERROR_MESSAGE);
} else {
this.$errors.failWithHelp(ANDROID_APP_BUNDLE_SIGNING_ERROR_MESSAGE);
}
if (this.$options.release && !hasValidAndroidSigning(this.$options)) {
this.$errors.failWithHelp(ANDROID_RELEASE_BUILD_ERROR_MESSAGE);
}

canExecute = await super.validateArgs(args, platform);
Expand Down
2 changes: 2 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const NATIVESCRIPT_KEY_NAME = "nativescript";
export const NODE_MODULES_FOLDER_NAME = "node_modules";
export const TNS_MODULES_FOLDER_NAME = "tns_modules";
export const TNS_CORE_MODULES_NAME = "tns-core-modules";
export const TNS_CORE_THEME_NAME = "nativescript-theme-core";
export const SCOPED_TNS_CORE_THEME_NAME = "@nativescript/theme";
export const WEBPACK_PLUGIN_NAME = "nativescript-dev-webpack";
export const TNS_CORE_MODULES_WIDGETS_NAME = "tns-core-modules-widgets";
export const TNS_ANDROID_RUNTIME_NAME = "tns-android";
Expand Down
6 changes: 6 additions & 0 deletions lib/services/assets-generation/assets-generation-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ export class AssetsGenerationService implements IAssetsGenerationService {
const outputPath = assetItem.path;
const width = assetItem.width * scale;
const height = assetItem.height * scale;

if (!width || !height) {
this.$logger.warn(`Image ${assetItem.filename} is skipped as its width and height are invalid.`);
continue;
}

let image: Jimp;
switch (operation) {
case Operations.OverlayWith:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as util from "util";
import { Device } from "nativescript-preview-sdk";
import { PluginComparisonMessages } from "./preview-app-constants";
import { NODE_MODULES_DIR_NAME } from "../../../common/constants";
import { PLATFORMS_DIR_NAME, PACKAGE_JSON_FILE_NAME } from "../../../constants";
import { PLATFORMS_DIR_NAME, PACKAGE_JSON_FILE_NAME, TNS_CORE_THEME_NAME, SCOPED_TNS_CORE_THEME_NAME } from "../../../constants";

export class PreviewAppPluginsService implements IPreviewAppPluginsService {
constructor(private $errors: IErrors,
Expand Down Expand Up @@ -41,10 +41,11 @@ export class PreviewAppPluginsService implements IPreviewAppPluginsService {

public getExternalPlugins(device: Device): string[] {
const devicePlugins = this.getDevicePlugins(device);
const themeNamesArray = [TNS_CORE_THEME_NAME, SCOPED_TNS_CORE_THEME_NAME];
const result = _.keys(devicePlugins)
// The core theme links are custom and
// should be handled by webpack during build.
.filter(plugin => plugin !== "nativescript-theme-core");
.filter(plugin => themeNamesArray.indexOf(plugin) === -1);

return result;
}
Expand Down
28 changes: 18 additions & 10 deletions lib/services/project-data-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,33 +236,36 @@ export class ProjectDataService implements IProjectDataService {
private async getIOSAssetSubGroup(dirPath: string): Promise<IAssetSubGroup> {
const pathToContentJson = path.join(dirPath, AssetConstants.iOSResourcesFileName);
const content = this.$fs.exists(pathToContentJson) && <IAssetSubGroup>this.$fs.readJson(pathToContentJson) || { images: [] };
const finalContent: IAssetSubGroup = { images: [] };

const imageDefinitions = this.getImageDefinitions().ios;

_.each(content && content.images, image => {
let foundMatchingDefinition = false;
// In some cases the image may not be available, it will just be described.
// When this happens, the filename will be empty.
// So we'll keep the path empty as well.
if (image.filename) {
image.path = path.join(dirPath, image.filename);
}

if (image.size) {
// size is basically <width>x<height>
const [width, height] = image.size.toString().split(AssetConstants.sizeDelimiter);
if (width && height) {
image.width = +width;
image.height = +height;
}
}

// Find the image size based on the hardcoded values in the image-definitions.json
_.each(imageDefinitions, (assetSubGroup: IAssetItem[]) => {
const assetItem = _.find(assetSubGroup, assetElement =>
assetElement.filename === image.filename && path.basename(assetElement.directory) === path.basename(dirPath)
);

if (image.size) {
// size is basically <width>x<height>
const [width, height] = image.size.toString().split(AssetConstants.sizeDelimiter);
if (width && height) {
image.width = +width;
image.height = +height;
}
}

if (assetItem) {
foundMatchingDefinition = true;
if (!image.width || !image.height) {
image.width = assetItem.width;
image.height = assetItem.height;
Expand All @@ -273,13 +276,18 @@ export class ProjectDataService implements IProjectDataService {
image.overlayImageScale = image.overlayImageScale || assetItem.overlayImageScale;
image.scale = image.scale || assetItem.scale;
image.rgba = assetItem.rgba;
finalContent.images.push(image);
// break each
return false;
}
});

if (!foundMatchingDefinition && image.filename) {
this.$logger.warn(`Didn't find a matching image definition for file ${path.join(path.basename(dirPath), image.filename)}. This file will be skipped from reources generation.`);
}
});

return content;
return finalContent;
}

private getAndroidAssetSubGroup(assetItems: IAssetItem[], realPaths: string[]): IAssetSubGroup {
Expand Down
12 changes: 6 additions & 6 deletions lib/services/webpack/webpack-compiler-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { WEBPACK_COMPILATION_COMPLETE, WEBPACK_PLUGIN_NAME } from "../../constan

export class WebpackCompilerService extends EventEmitter implements IWebpackCompilerService {
private webpackProcesses: IDictionary<child_process.ChildProcess> = {};
private expectedHash: string = null;
private expectedHashes: IStringDictionary = {};

constructor(
private $errors: IErrors,
Expand Down Expand Up @@ -42,14 +42,14 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
if (message.emittedFiles) {
if (isFirstWebpackWatchCompilation) {
isFirstWebpackWatchCompilation = false;
this.expectedHash = message.hash;
this.expectedHashes[platformData.platformNameLowerCase] = message.hash;
return;
}

let result;

if (prepareData.hmr) {
result = this.getUpdatedEmittedFiles(message.emittedFiles, message.chunkFiles, message.hash);
result = this.getUpdatedEmittedFiles(message.emittedFiles, message.chunkFiles, message.hash, platformData.platformNameLowerCase);
} else {
result = { emittedFiles: message.emittedFiles, fallbackFiles: <string[]>[], hash: "" };
}
Expand Down Expand Up @@ -248,7 +248,7 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
return args;
}

public getUpdatedEmittedFiles(allEmittedFiles: string[], chunkFiles: string[], nextHash: string) {
public getUpdatedEmittedFiles(allEmittedFiles: string[], chunkFiles: string[], nextHash: string, platform: string) {
const currentHash = this.getCurrentHotUpdateHash(allEmittedFiles);

// This logic is needed as there are already cases when webpack doesn't emit any files physically.
Expand All @@ -260,8 +260,8 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
// if files will be emitted or not. This way, the first successful compilation after fixing the compilation error generates
// a hash that is not the same as the one expected in the latest emitted hot-update.json file.
// As a result, the hmr chain is broken and the changes are not applied.
const isHashValid = nextHash ? this.expectedHash === currentHash : true;
this.expectedHash = nextHash;
const isHashValid = nextHash ? this.expectedHashes[platform] === currentHash : true;
this.expectedHashes[platform] = nextHash;

const emittedHotUpdatesAndAssets = isHashValid ? _.difference(allEmittedFiles, chunkFiles) : allEmittedFiles;

Expand Down
37 changes: 27 additions & 10 deletions test/services/webpack/webpack-compiler-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Yok } from "../../../lib/common/yok";
import { WebpackCompilerService } from "../../../lib/services/webpack/webpack-compiler-service";
import { assert } from "chai";

const iOSPlatformName = "ios";
const androidPlatformName = "android";
const chunkFiles = ["bundle.js", "runtime.js", "vendor.js"];

function getAllEmittedFiles(hash: string) {
Expand Down Expand Up @@ -35,39 +37,54 @@ describe("WebpackCompilerService", () => {
describe("getUpdatedEmittedFiles", () => {
// backwards compatibility with old versions of nativescript-dev-webpack
it("should return only hot updates when nextHash is not provided", async () => {
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, null);
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, null, iOSPlatformName);
const expectedEmittedFiles = ['bundle.hash1.hot-update.js', 'hash1.hot-update.json'];

assert.deepEqual(result.emittedFiles, expectedEmittedFiles);
});
// 2 successful webpack compilations
it("should return only hot updates when nextHash is provided", async () => {
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2");
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash2"), chunkFiles, "hash3");
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName);
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash2"), chunkFiles, "hash3", iOSPlatformName);

assert.deepEqual(result.emittedFiles, ['bundle.hash2.hot-update.js', 'hash2.hot-update.json']);
});
// 1 successful webpack compilation, n compilations with no emitted files
it("should return all files when there is a webpack compilation with no emitted files", () => {
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2");
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash4"), chunkFiles, "hash5");
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName);
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash4"), chunkFiles, "hash5", iOSPlatformName);

assert.deepEqual(result.emittedFiles, ['bundle.js', 'runtime.js', 'bundle.hash4.hot-update.js', 'hash4.hot-update.json']);
});
// 1 successful webpack compilation, n compilations with no emitted files, 1 successful webpack compilation
it("should return only hot updates after fixing the compilation error", () => {
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2");
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash5"), chunkFiles, "hash6");
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash6"), chunkFiles, "hash7");
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName);
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash5"), chunkFiles, "hash6", iOSPlatformName);
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash6"), chunkFiles, "hash7", iOSPlatformName);

assert.deepEqual(result.emittedFiles, ['bundle.hash6.hot-update.js', 'hash6.hot-update.json']);
});
// 1 webpack compilation with no emitted files
it("should return all files when first compilation on livesync change is not successful", () => {
(<any>webpackCompilerService).expectedHash = "hash1";
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2");
(<any>webpackCompilerService).expectedHashes = {
"ios": "hash1"
};
const result = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName);

assert.deepEqual(result.emittedFiles, ["bundle.hash1.hot-update.js", "hash1.hot-update.json"]);
});
it("should return correct hashes when there are more than one platform", () => {
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash1"), chunkFiles, "hash2", iOSPlatformName);
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash3"), chunkFiles, "hash4", androidPlatformName);

webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash2"), chunkFiles, "hash5", iOSPlatformName);
webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash4"), chunkFiles, "hash6", androidPlatformName);

const iOSResult = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash5"), chunkFiles, "hash7", iOSPlatformName);
assert.deepEqual(iOSResult.emittedFiles, ["bundle.hash5.hot-update.js", "hash5.hot-update.json"]);

const androidResult = webpackCompilerService.getUpdatedEmittedFiles(getAllEmittedFiles("hash6"), chunkFiles, "hash8", androidPlatformName);
assert.deepEqual(androidResult.emittedFiles, ["bundle.hash6.hot-update.js", "hash6.hot-update.json"]);
});
});
});