Skip to content

Commit 8b72f12

Browse files
author
Fatme
authored
Merge pull request #4685 from NativeScript/fatme/merge-release-541
chore: merge release into master
2 parents c9098bb + bce981b commit 8b72f12

File tree

11 files changed

+146
-47
lines changed

11 files changed

+146
-47
lines changed

lib/common/logger/logger.ts

+23-14
Original file line numberDiff line numberDiff line change
@@ -154,31 +154,40 @@ export class Logger implements ILogger {
154154
}
155155

156156
private getLogOptionsForMessage(data: any[]): { data: any[], [key: string]: any } {
157-
const opts = _.keys(LoggerConfigData);
157+
const loggerOptionKeys = _.keys(LoggerConfigData);
158+
const dataToCheck = data.filter(el => {
159+
// objects created with Object.create(null) do not have `hasOwnProperty` function
160+
if (!!el && typeof el === "object" && el.hasOwnProperty && typeof el.hasOwnProperty === "function") {
161+
for (const key of loggerOptionKeys) {
162+
if (el.hasOwnProperty(key)) {
163+
// include only the elements which have one of the keys we've specified as logger options
164+
return true;
165+
}
166+
}
167+
}
158168

159-
const result: any = {};
160-
const cleanedData = _.cloneDeep(data);
169+
return false;
170+
});
161171

162-
// objects created with Object.create(null) do not have `hasOwnProperty` function
163-
const dataToCheck = data.filter(el => typeof el === "object" && el.hasOwnProperty && typeof el.hasOwnProperty === "function");
172+
const result: any = {
173+
data: _.difference(data, dataToCheck)
174+
};
164175

165176
for (const element of dataToCheck) {
166-
if (opts.length === 0) {
177+
if (loggerOptionKeys.length === 0) {
167178
break;
168179
}
169180

170-
const remainingOpts = _.cloneDeep(opts);
181+
const remainingOpts = _.cloneDeep(loggerOptionKeys);
171182
for (const prop of remainingOpts) {
172183
const hasProp = element && element.hasOwnProperty(prop);
173184
if (hasProp) {
174-
opts.splice(opts.indexOf(prop), 1);
185+
loggerOptionKeys.splice(loggerOptionKeys.indexOf(prop), 1);
175186
result[prop] = element[prop];
176-
cleanedData.splice(cleanedData.indexOf(element), 1);
177187
}
178188
}
179189
}
180190

181-
result.data = cleanedData;
182191
return result;
183192
}
184193

@@ -195,19 +204,19 @@ export class Logger implements ILogger {
195204
/*******************************************************************************************
196205
* Metods below are deprecated. Delete them in 6.0.0 release: *
197206
* Present only for backwards compatibility as some plugins (nativescript-plugin-firebase) *
198-
* use these methods in their hooks *
207+
* use these methods in their hooks *
199208
*******************************************************************************************/
200209

201210
out(...args: any[]): void {
202-
this.info(args);
211+
this.info(...args);
203212
}
204213

205214
write(...args: any[]): void {
206-
this.info(args, { [LoggerConfigData.skipNewLine]: true });
215+
this.info(...args, { [LoggerConfigData.skipNewLine]: true });
207216
}
208217

209218
printOnStderr(...args: string[]): void {
210-
this.error(args);
219+
this.error(...args);
211220
}
212221

213222
printInfoMessageOnSameLine(message: string): void {

lib/common/services/commands-service.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,6 @@ export class CommandsService implements ICommandsService {
2929
private $extensibilityService: IExtensibilityService,
3030
private $optionsTracker: IOptionsTracker,
3131
private $projectDataService: IProjectDataService) {
32-
let projectData = null;
33-
try {
34-
projectData = this.$projectDataService.getProjectData();
35-
} catch (err) {
36-
this.$logger.trace(`Error while trying to get project data. More info: ${err}`);
37-
}
38-
39-
this.$options.setupOptions(projectData);
40-
this.$options.printMessagesForDeprecatedOptions(this.$logger);
4132
}
4233

4334
public allCommands(opts: { includeDevCommands: boolean }): string[] {
@@ -114,8 +105,17 @@ export class CommandsService implements ICommandsService {
114105

115106
private async tryExecuteCommandAction(commandName: string, commandArguments: string[]): Promise<boolean | ICanExecuteCommandOutput> {
116107
const command = this.$injector.resolveCommand(commandName);
117-
if (!command || (command && !command.isHierarchicalCommand)) {
118-
this.$options.validateOptions(command ? command.dashedOptions : null);
108+
if (!command || !command.isHierarchicalCommand) {
109+
let projectData = null;
110+
try {
111+
projectData = this.$projectDataService.getProjectData();
112+
} catch (err) {
113+
this.$logger.trace(`Error while trying to get project data. More info: ${err}`);
114+
}
115+
116+
const dashedOptions = command ? command.dashedOptions : null;
117+
this.$options.validateOptions(dashedOptions, projectData);
118+
this.$options.printMessagesForDeprecatedOptions(this.$logger);
119119
}
120120

121121
return this.canExecuteCommand(commandName, commandArguments);

lib/common/test/unit-tests/logger.ts

+82-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Yok } from "../../yok";
22
import { Logger } from "../../logger/logger";
33
import * as path from "path";
4+
import * as util from "util";
45
import { assert } from "chai";
56
import * as fileSystemFile from "../../file-system";
7+
import { LoggerConfigData } from "../../../constants";
68

79
const passwordReplacement = "*******";
810
const debugTrace = ["debug", "trace"];
@@ -33,7 +35,11 @@ describe("logger", () => {
3335
fs = testInjector.resolve("fs");
3436
outputs = {
3537
debug: "",
36-
trace: ""
38+
trace: "",
39+
info: "",
40+
error: "",
41+
context: {},
42+
removedContext: {}
3743
};
3844

3945
const log4jsLogger = {
@@ -42,6 +48,18 @@ describe("logger", () => {
4248
},
4349
trace: (...args: string[]) => {
4450
outputs.trace += args.join("");
51+
},
52+
info: (...args: string[]) => {
53+
outputs.info += util.format.apply(null, args);
54+
},
55+
error: (...args: string[]) => {
56+
outputs.error += util.format.apply(null, args);
57+
},
58+
addContext(key: string, value: any): void {
59+
outputs.context[key] = value;
60+
},
61+
removeContext(key: string): void {
62+
outputs.removedContext[key] = true;
4563
}
4664
};
4765

@@ -139,4 +157,67 @@ describe("logger", () => {
139157
assert.deepEqual(outputs.trace, `${request}${requestBody}`, "logger.trace should not obfuscate body of request unless it is towards api/itmstransporter");
140158
});
141159
});
160+
161+
describe("info", () => {
162+
[undefined, null, false, "string value", 42, { obj: 1 }, ["string value 1", "string value 2"]].forEach(value => {
163+
it(`handles formatted message with '${value}' value in one of the args`, () => {
164+
logger.info("test %s", value);
165+
assert.equal(outputs.info, `test ${value}`);
166+
assert.deepEqual(outputs.context, {}, "Nothing should be added to logger context.");
167+
assert.deepEqual(outputs.removedContext, {}, "Removed context should be empty.");
168+
});
169+
170+
it(`handles formatted message with '${value}' value in one of the args and additional values passed to context`, () => {
171+
logger.info("test %s", value, { [LoggerConfigData.skipNewLine]: true });
172+
assert.equal(outputs.info, `test ${value}`);
173+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true }, `${LoggerConfigData.skipNewLine} should be set with value true.`);
174+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true }, `Removed context should contain ${LoggerConfigData.skipNewLine}`);
175+
});
176+
});
177+
178+
it("sets correct context when multiple logger options are passed in one object", () => {
179+
logger.info("test", { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
180+
assert.equal(outputs.info, "test");
181+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
182+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
183+
});
184+
185+
it("sets correct context when multiple logger options are passed in multiple objects", () => {
186+
logger.info({ [LoggerConfigData.useStderr]: true }, "test", { [LoggerConfigData.skipNewLine]: true });
187+
assert.equal(outputs.info, "test");
188+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
189+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true, [LoggerConfigData.useStderr]: true });
190+
});
191+
});
192+
193+
describe("printMarkdown", () => {
194+
it(`passes markdown formatted text to log4js.info method`, () => {
195+
logger.printMarkdown("header text\n==");
196+
assert.isTrue(outputs.info.indexOf("# header text") !== -1);
197+
});
198+
199+
it(`passes skipNewLine to log4js context`, () => {
200+
logger.printMarkdown("`coloured` text");
201+
assert.isTrue(outputs.info.indexOf("coloured") !== -1);
202+
assert.deepEqual(outputs.context, { [LoggerConfigData.skipNewLine]: true }, `${LoggerConfigData.skipNewLine} should be set with value true.`);
203+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.skipNewLine]: true }, `Removed context should contain ${LoggerConfigData.skipNewLine}`);
204+
});
205+
});
206+
207+
describe("error", () => {
208+
const errorMessage = "this is error message";
209+
it(`passes useStderr to log4js context`, () => {
210+
logger.error(errorMessage);
211+
assert.equal(outputs.error, errorMessage);
212+
assert.deepEqual(outputs.context, { [LoggerConfigData.useStderr]: true }, `${LoggerConfigData.useStderr} should be set with value true.`);
213+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.useStderr]: true }, `Removed context should contain ${LoggerConfigData.useStderr}`);
214+
});
215+
216+
it(`allows overwrite of useStderr`, () => {
217+
logger.error(errorMessage, { [LoggerConfigData.useStderr]: false });
218+
assert.equal(outputs.error, errorMessage);
219+
assert.deepEqual(outputs.context, { [LoggerConfigData.useStderr]: false }, `${LoggerConfigData.useStderr} should be set with value false.`);
220+
assert.deepEqual(outputs.removedContext, { [LoggerConfigData.useStderr]: true }, `Removed context should contain ${LoggerConfigData.useStderr}`);
221+
});
222+
});
142223
});

lib/common/verify-node-version.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export function verifyNodeVersion(): void {
4949

5050
var nodeWarning = getNodeWarning();
5151
if (nodeWarning && nodeWarning.message) {
52-
console.warn((`${os.EOL}${nodeWarning.message}${os.EOL}`).yellow.bold);
52+
console.warn((os.EOL + nodeWarning.message + os.EOL).yellow.bold);
5353
}
5454
}
5555

lib/declarations.d.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ interface IAndroidBundleOptions {
503503

504504
interface IOptions extends IRelease, IDeviceIdentifier, IJustLaunch, IAvd, IAvailableDevices, IProfileDir, IHasEmulatorOption, IBundleString, IPlatformTemplate, IHasEmulatorOption, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, IAndroidBundleOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions, IPluginSeedOptions, IGenerateOptions {
505505
argv: IYargArgv;
506-
validateOptions(commandSpecificDashedOptions?: IDictionary<IDashedOption>): void;
506+
validateOptions(commandSpecificDashedOptions?: IDictionary<IDashedOption>, projectData?: IProjectData): void;
507507
options: IDictionary<IDashedOption>;
508508
shorthands: string[];
509509
/**
@@ -573,7 +573,6 @@ interface IOptions extends IRelease, IDeviceIdentifier, IJustLaunch, IAvd, IAvai
573573
performance: Object;
574574
cleanupLogFile: string;
575575
workflow: any;
576-
setupOptions(projectData: IProjectData): void;
577576
printMessagesForDeprecatedOptions(logger: ILogger): void;
578577
}
579578

lib/definitions/project.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ interface IAssetItem {
212212
idiom: string;
213213
resizeOperation?: string;
214214
overlayImageScale?: number;
215+
rgba?: boolean;
215216
}
216217

217218
interface IAssetSubGroup {

lib/options.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ export class Options {
2121

2222
public options: IDictionary<IDashedOption>;
2323

24-
public setupOptions(projectData: IProjectData): void {
24+
public setupOptions(projectData: IProjectData, commandSpecificDashedOptions?: IDictionary<IDashedOption>): void {
25+
if (commandSpecificDashedOptions) {
26+
_.extend(this.options, commandSpecificDashedOptions);
27+
this.setArgv();
28+
}
29+
2530
if (this.argv.release && this.argv.hmr) {
2631
this.$errors.failWithoutHelp("The options --release and --hmr cannot be used simultaneously.");
2732
}
@@ -173,12 +178,8 @@ export class Options {
173178
return this.argv[optionName];
174179
}
175180

176-
public validateOptions(commandSpecificDashedOptions?: IDictionary<IDashedOption>): void {
177-
if (commandSpecificDashedOptions) {
178-
_.extend(this.options, commandSpecificDashedOptions);
179-
this.setArgv();
180-
}
181-
181+
public validateOptions(commandSpecificDashedOptions?: IDictionary<IDashedOption>, projectData?: IProjectData): void {
182+
this.setupOptions(projectData, commandSpecificDashedOptions);
182183
const parsed = Object.create(null);
183184
// DO NOT REMOVE { } as when they are missing and some of the option values is false, the each stops as it thinks we have set "return false".
184185
_.each(_.keys(this.argv), optionName => {

lib/services/assets-generation/assets-generation-service.ts

+14-8
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,30 @@ export class AssetsGenerationService implements IAssetsGenerationService {
7171
const outputPath = assetItem.path;
7272
const width = assetItem.width * scale;
7373
const height = assetItem.height * scale;
74-
74+
let image: Jimp;
7575
switch (operation) {
7676
case Operations.OverlayWith:
7777
const overlayImageScale = assetItem.overlayImageScale || AssetConstants.defaultOverlayImageScale;
7878
const imageResize = Math.round(Math.min(width, height) * overlayImageScale);
79-
const image = await this.resize(generationData.imagePath, imageResize, imageResize);
80-
await this.generateImage(generationData.background, width, height, outputPath, image);
79+
image = await this.resize(generationData.imagePath, imageResize, imageResize);
80+
image = this.generateImage(generationData.background, width, height, outputPath, image);
8181
break;
8282
case Operations.Blank:
83-
await this.generateImage(generationData.background, width, height, outputPath);
83+
image = this.generateImage(generationData.background, width, height, outputPath);
8484
break;
8585
case Operations.Resize:
86-
const resizedImage = await this.resize(generationData.imagePath, width, height);
87-
resizedImage.write(outputPath);
86+
image = await this.resize(generationData.imagePath, width, height);
8887
break;
8988
default:
9089
throw new Error(`Invalid image generation operation: ${operation}`);
9190
}
91+
92+
// This code disables the alpha chanel, as some images for the Apple App Store must not have transparency.
93+
if (assetItem.rgba === false) {
94+
image = image.rgba(false);
95+
}
96+
97+
image.write(outputPath);
9298
}
9399
}
94100

@@ -97,7 +103,7 @@ export class AssetsGenerationService implements IAssetsGenerationService {
97103
return image.scaleToFit(width, height);
98104
}
99105

100-
private generateImage(background: string, width: number, height: number, outputPath: string, overlayImage?: Jimp): void {
106+
private generateImage(background: string, width: number, height: number, outputPath: string, overlayImage?: Jimp): Jimp {
101107
// Typescript declarations for Jimp are not updated to define the constructor with backgroundColor so we workaround it by casting it to <any> for this case only.
102108
const J = <any>Jimp;
103109
const backgroundColor = this.getRgbaNumber(background);
@@ -109,7 +115,7 @@ export class AssetsGenerationService implements IAssetsGenerationService {
109115
image = image.composite(overlayImage, centeredWidth, centeredHeight);
110116
}
111117

112-
image.write(outputPath);
118+
return image;
113119
}
114120

115121
private getRgbaNumber(colorString: string): number {

lib/services/project-data-service.ts

+1
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export class ProjectDataService implements IProjectDataService {
252252
image.resizeOperation = image.resizeOperation || assetItem.resizeOperation;
253253
image.overlayImageScale = image.overlayImageScale || assetItem.overlayImageScale;
254254
image.scale = image.scale || assetItem.scale;
255+
image.rgba = assetItem.rgba;
255256
// break each
256257
return false;
257258
}

resources/assets/image-definitions.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@
139139
"height": 1024,
140140
"directory": "Assets.xcassets/AppIcon.appiconset",
141141
"filename": "icon-1024.png",
142-
"scale": "1x"
142+
"scale": "1x",
143+
"rgba": false
143144
}
144145
],
145146
"splashBackgrounds": [

test/options.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ describe("options", () => {
325325
it(`should pass correctly when ${testCase.name} and useLegacyWorkflow is ${useLegacyWorkflow}`, () => {
326326
(testCase.args || []).forEach(arg => process.argv.push(arg));
327327

328-
const options = createOptions(testInjector);
328+
const options: any = createOptions(testInjector);
329329
const projectData = <IProjectData>{ useLegacyWorkflow };
330330
options.setupOptions(projectData);
331331

@@ -359,7 +359,7 @@ describe("options", () => {
359359
errors.failWithoutHelp = (error: string) => actualError = error;
360360
(testCase.args || []).forEach(arg => process.argv.push(arg));
361361

362-
const options = createOptions(testInjector);
362+
const options: any = createOptions(testInjector);
363363
options.setupOptions(null);
364364

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

0 commit comments

Comments
 (0)