Skip to content

Commit 02dd99c

Browse files
committed
feat(log): allow null, which silences output
resolves #86
1 parent f4f7d20 commit 02dd99c

File tree

8 files changed

+55
-67
lines changed

8 files changed

+55
-67
lines changed

src/__tests__/image.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ describe('cordova-res', () => {
2828
});
2929

3030
it('should throw with empty array of source images', async () => {
31-
await expect(image.resolveSourceImage(Platform.ANDROID, ResourceType.ICON, [])).rejects.toThrow('Missing source image');
31+
await expect(image.resolveSourceImage(Platform.ANDROID, ResourceType.ICON, [], null)).rejects.toThrow('Missing source image');
3232
expect(fsMock.readFile).not.toHaveBeenCalled();
3333
});
3434

3535
it('should throw with source image with error', async () => {
3636
fsMock.readFile.mockImplementation(async () => { throw new Error('err'); });
37-
await expect(image.resolveSourceImage(Platform.ANDROID, ResourceType.ICON, ['blah.png'])).rejects.toThrow('Missing source image');
37+
await expect(image.resolveSourceImage(Platform.ANDROID, ResourceType.ICON, ['blah.png'], null)).rejects.toThrow('Missing source image');
3838
expect(fsMock.readFile).toHaveBeenCalledTimes(1);
3939
});
4040

4141
it('should resolve with proper image', async () => {
4242
fsMock.readFile.mockImplementationOnce(async () => { throw new Error('err'); });
4343
fsMock.readFile.mockImplementationOnce(async () => Buffer.from([]));
44-
const { src } = await image.resolveSourceImage(Platform.ANDROID, ResourceType.ICON, ['foo.png', 'bar.png']);
44+
const { src } = await image.resolveSourceImage(Platform.ANDROID, ResourceType.ICON, ['foo.png', 'bar.png'], null);
4545
expect(src).toEqual('bar.png');
4646
expect(fsMock.readFile).toHaveBeenCalledTimes(2);
4747
expect(resourcesMock.validateResource).toHaveBeenCalledTimes(1);

src/__tests__/platform.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ describe('cordova-res', () => {
3737

3838
const result = await platform.run(Platform.ANDROID, 'resources', {
3939
[ResourceType.ICON]: { sources: ['icon.png'] },
40-
});
40+
}, null);
4141

4242
const generatedImages = getResourcesConfig(Platform.ANDROID, ResourceType.ICON).resources;
4343

4444
expect(imageMock.resolveSourceImage).toHaveBeenCalledTimes(1);
45-
expect(imageMock.resolveSourceImage).toHaveBeenCalledWith('android', 'icon', ['icon.png'], undefined);
45+
expect(imageMock.resolveSourceImage).toHaveBeenCalledWith('android', 'icon', ['icon.png'], null);
4646
expect(imageMock.generateImage).toHaveBeenCalledTimes(generatedImages.length);
4747

4848
for (const generatedImage of generatedImages) {
@@ -55,7 +55,7 @@ describe('cordova-res', () => {
5555
},
5656
expect.anything(),
5757
expect.anything(),
58-
undefined
58+
null
5959
);
6060
}
6161

@@ -68,12 +68,12 @@ describe('cordova-res', () => {
6868

6969
const result = await platform.run(Platform.WINDOWS, 'resources', {
7070
[ResourceType.ICON]: { sources: ['icon.png'] },
71-
});
71+
}, null);
7272

7373
const generatedImages = getResourcesConfig(Platform.WINDOWS, ResourceType.ICON).resources;
7474

7575
expect(imageMock.resolveSourceImage).toHaveBeenCalledTimes(1);
76-
expect(imageMock.resolveSourceImage).toHaveBeenCalledWith('windows', 'icon', ['icon.png'], undefined);
76+
expect(imageMock.resolveSourceImage).toHaveBeenCalledWith('windows', 'icon', ['icon.png'], null);
7777
expect(imageMock.generateImage).toHaveBeenCalledTimes(generatedImages.length);
7878

7979
for (const generatedImage of generatedImages) {
@@ -86,7 +86,7 @@ describe('cordova-res', () => {
8686
},
8787
expect.anything(),
8888
expect.anything(),
89-
undefined
89+
null
9090
);
9191
}
9292

src/cordova/config.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function getConfigPath(directory: string): string {
1414
return pathlib.resolve(directory, 'config.xml');
1515
}
1616

17-
export async function run(configPath: string, resourcesDirectory: string, doc: et.ElementTree, sources: readonly ResolvedSource[], resources: readonly GeneratedResource[], errstream?: NodeJS.WritableStream): Promise<void> {
17+
export async function run(configPath: string, resourcesDirectory: string, doc: et.ElementTree, sources: readonly ResolvedSource[], resources: readonly GeneratedResource[], errstream: NodeJS.WritableStream | null): Promise<void> {
1818
const colors = sources.filter((source): source is ResolvedColorSource => source.type === SourceType.COLOR);
1919

2020
if (colors.length > 0) {
@@ -71,15 +71,15 @@ export async function runColorsConfig(colorsPath: string, colors: readonly Resol
7171
await write(colorsPath, colorsDocument);
7272
}
7373

74-
export function runConfig(configPath: string, doc: et.ElementTree, resources: readonly GeneratedResource[], errstream?: NodeJS.WritableStream): void {
74+
export function runConfig(configPath: string, doc: et.ElementTree, resources: readonly GeneratedResource[], errstream: NodeJS.WritableStream | null): void {
7575
const root = doc.getroot();
7676
const orientationPreference = getPreference(root, 'Orientation');
7777
debug('Orientation preference: %O', orientationPreference);
7878

7979
const orientation = orientationPreference || 'default';
8080

81-
if (orientation !== 'default' && errstream) {
82-
errstream.write(util.format(`WARN:\tOrientation preference set to '%s'. Only configuring %s resources.`, orientation, orientation) + '\n');
81+
if (orientation !== 'default') {
82+
errstream?.write(util.format(`WARN:\tOrientation preference set to '%s'. Only configuring %s resources.`, orientation, orientation) + '\n');
8383
}
8484

8585
const platforms = groupImages(resources);

src/image.ts

+8-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export type SharpTransformation = (pipeline: Sharp) => Sharp;
1414
/**
1515
* Check an array of source files, returning the first viable image.
1616
*/
17-
export async function resolveSourceImage(platform: Platform, type: ResourceType, sources: string[], errstream?: NodeJS.WritableStream): Promise<ResolvedImageSource> {
17+
export async function resolveSourceImage(platform: Platform, type: ResourceType, sources: string[], errstream: NodeJS.WritableStream | null): Promise<ResolvedImageSource> {
1818
const errors: [string, NodeJS.ErrnoException][] = [];
1919

2020
for (const source of sources) {
@@ -35,7 +35,7 @@ export async function resolveSourceImage(platform: Platform, type: ResourceType,
3535
);
3636
}
3737

38-
export async function readSourceImage(platform: Platform, type: ResourceType, src: string, errstream?: NodeJS.WritableStream): Promise<ResolvedImageSource> {
38+
export async function readSourceImage(platform: Platform, type: ResourceType, src: string, errstream: NodeJS.WritableStream | null): Promise<ResolvedImageSource> {
3939
const image = sharp(await readFile(src));
4040
const metadata = await validateResource(platform, type, src, image, errstream);
4141

@@ -50,15 +50,12 @@ export async function readSourceImage(platform: Platform, type: ResourceType, sr
5050
};
5151
}
5252

53-
export function debugSourceImage(src: string, error: NodeJS.ErrnoException, errstream?: NodeJS.WritableStream): void {
53+
export function debugSourceImage(src: string, error: NodeJS.ErrnoException, errstream: NodeJS.WritableStream | null): void {
5454
if (error.code === 'ENOENT') {
5555
debug('Source file missing: %s', src);
5656
} else {
57-
if (errstream) {
58-
errstream.write(util.format('WARN:\tError with source file %s: %s', src, error) + '\n');
59-
} else {
60-
debug('Error with source file %s: %O', src, error);
61-
}
57+
errstream?.write(util.format('WARN:\tError with source file %s: %s', src, error) + '\n');
58+
debug('Error with source file %s: %O', src, error);
6259
}
6360
}
6461

@@ -69,18 +66,16 @@ export interface ImageSchema {
6966
height: number;
7067
}
7168

72-
export async function generateImage(image: ImageSchema, src: Sharp, metadata: Metadata, errstream?: NodeJS.WritableStream): Promise<void> {
69+
export async function generateImage(image: ImageSchema, src: Sharp, metadata: Metadata, errstream: NodeJS.WritableStream | null): Promise<void> {
7370
if (image.format === Format.NONE) {
7471
debug('Skipping generation of %o (format=none)', image.src);
7572
return;
7673
}
7774

7875
debug('Generating %o (%ox%o)', image.src, image.width, image.height);
7976

80-
if (errstream) {
81-
if (metadata.format !== image.format) {
82-
errstream.write(util.format(`WARN:\tMust perform conversion from %s to png.`, metadata.format) + '\n');
83-
}
77+
if (metadata.format !== image.format) {
78+
errstream?.write(util.format(`WARN:\tMust perform conversion from %s to png.`, metadata.format) + '\n');
8479
}
8580

8681
const transformations = [createImageResizer(image), createImageConverter(image.format)];

src/index.ts

+7-10
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,8 @@ async function CordovaRes(options: CordovaRes.Options = {}): Promise<Result> {
6060
if (await pathWritable(configPath)) {
6161
config = await readConfig(configPath);
6262
} else {
63+
errstream?.write(`WARN:\tNo config.xml file in directory. Skipping config.\n`);
6364
debug('File missing/not writable: %O', configPath);
64-
65-
if (errstream) {
66-
errstream.write(`WARN:\tNo config.xml file in directory. Skipping config.\n`);
67-
}
6865
}
6966
}
7067

@@ -75,7 +72,7 @@ async function CordovaRes(options: CordovaRes.Options = {}): Promise<Result> {
7572
if (platformOptions) {
7673
const platformResult = await runPlatform(platform, resourcesDirectory, platformOptions, errstream);
7774

78-
logstream.write(util.format(`Generated %s resources for %s`, platformResult.resources.length, prettyPlatform(platform)) + '\n');
75+
logstream?.write(util.format(`Generated %s resources for %s`, platformResult.resources.length, prettyPlatform(platform)) + '\n');
7976

8077
resources.push(...platformResult.resources);
8178
sources.push(...platformResult.sources);
@@ -90,7 +87,7 @@ async function CordovaRes(options: CordovaRes.Options = {}): Promise<Result> {
9087
await runConfig(configPath, resourcesDirectory, config, sources, resources, errstream);
9188
await writeConfig(configPath, config);
9289

93-
logstream.write(`Wrote to config.xml\n`);
90+
logstream?.write(`Wrote to config.xml\n`);
9491
}
9592

9693
return {
@@ -152,16 +149,16 @@ namespace CordovaRes {
152149
/**
153150
* Specify an alternative output mechanism.
154151
*
155-
* A NullStream may be used to silence output entirely.
152+
* `null` may be specified to silence output.
156153
*/
157-
readonly logstream?: NodeJS.WritableStream;
154+
readonly logstream?: NodeJS.WritableStream | null;
158155

159156
/**
160157
* Specify an alternative error output mechanism.
161158
*
162-
* A NullStream may be used to silence error output entirely.
159+
* `null` may be specified to silence error output.
163160
*/
164-
readonly errstream?: NodeJS.WritableStream;
161+
readonly errstream?: NodeJS.WritableStream | null;
165162

166163
/**
167164
* Resource generation configuration by platform.

src/native.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -180,20 +180,18 @@ async function copyImages(sourcePath: string, targetPath: string, images: readon
180180
}));
181181
}
182182

183-
export async function copyToNativeProject(platform: Platform, nativeProject: NativeProjectConfig, logstream: NodeJS.WritableStream, errstream?: NodeJS.WritableStream) {
183+
export async function copyToNativeProject(platform: Platform, nativeProject: NativeProjectConfig, logstream: NodeJS.WritableStream | null, errstream: NodeJS.WritableStream | null) {
184184
if (platform === Platform.IOS) {
185185
const iosProjectDirectory = nativeProject.directory || 'ios';
186186
await copyImages(SOURCE_IOS_ICON, path.join(iosProjectDirectory, TARGET_IOS_ICON), IOS_ICONS);
187187
await copyImages(SOURCE_IOS_SPLASH, path.join(iosProjectDirectory, TARGET_IOS_SPLASH), IOS_SPLASHES);
188-
logstream.write(util.format(`Copied %s resource items to %s`, IOS_ICONS.length + IOS_SPLASHES.length, prettyPlatform(platform)) + '\n');
188+
logstream?.write(util.format(`Copied %s resource items to %s`, IOS_ICONS.length + IOS_SPLASHES.length, prettyPlatform(platform)) + '\n');
189189
} else if (platform === Platform.ANDROID) {
190190
const androidProjectDirectory = nativeProject.directory || 'android';
191191
await copyImages(SOURCE_ANDROID_ICON, path.join(androidProjectDirectory, TARGET_ANDROID_ICON), ANDROID_ICONS);
192192
await copyImages(SOURCE_ANDROID_SPLASH, path.join(androidProjectDirectory, TARGET_ANDROID_SPLASH), ANDROID_SPLASHES);
193-
logstream.write(util.format(`Copied %s resource items to %s`, ANDROID_ICONS.length + ANDROID_SPLASHES.length, prettyPlatform(platform)) + '\n');
193+
logstream?.write(util.format(`Copied %s resource items to %s`, ANDROID_ICONS.length + ANDROID_SPLASHES.length, prettyPlatform(platform)) + '\n');
194194
} else {
195-
if (errstream) {
196-
errstream.write(util.format('WARN:\tCopying to native projects is not supported for %s', prettyPlatform(platform)) + '\n');
197-
}
195+
errstream?.write(util.format('WARN:\tCopying to native projects is not supported for %s', prettyPlatform(platform)) + '\n');
198196
}
199197
}

src/platform.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export interface RunPlatformResult {
8686
/**
8787
* Run resource generation for the given platform.
8888
*/
89-
export async function run(platform: Platform, resourcesDirectory: string, options: Readonly<RunPlatformOptions>, errstream?: NodeJS.WritableStream): Promise<RunPlatformResult> {
89+
export async function run(platform: Platform, resourcesDirectory: string, options: Readonly<RunPlatformOptions>, errstream: NodeJS.WritableStream | null): Promise<RunPlatformResult> {
9090
debug('Running %s platform with options: %O', platform, options);
9191

9292
const resources: GeneratedResource[] = [];
@@ -124,7 +124,7 @@ export async function run(platform: Platform, resourcesDirectory: string, option
124124
* If there are no options given for this resource or if the source images are
125125
* not suitable, this function resolves with `undefined`.
126126
*/
127-
export async function safelyGenerateSimpleResources(type: ResourceType.ICON | ResourceType.SPLASH, platform: Platform, resourcesDirectory: string, options?: Readonly<SimpleResourceOptions>, errstream?: NodeJS.WritableStream): Promise<SimpleResourceResult | undefined> {
127+
export async function safelyGenerateSimpleResources(type: ResourceType.ICON | ResourceType.SPLASH, platform: Platform, resourcesDirectory: string, options: Readonly<SimpleResourceOptions> | undefined, errstream: NodeJS.WritableStream | null): Promise<SimpleResourceResult | undefined> {
128128
if (!options) {
129129
return;
130130
}
@@ -149,7 +149,7 @@ export async function safelyGenerateSimpleResources(type: ResourceType.ICON | Re
149149
* If there are no options given for this resource, this function resolves
150150
* with `undefined`.
151151
*/
152-
export async function generateSimpleResources(type: ResourceType.ICON | ResourceType.SPLASH, platform: Platform, resourcesDirectory: string, options?: Readonly<SimpleResourceOptions>, errstream?: NodeJS.WritableStream): Promise<SimpleResourceResult | undefined> {
152+
export async function generateSimpleResources(type: ResourceType.ICON | ResourceType.SPLASH, platform: Platform, resourcesDirectory: string, options: Readonly<SimpleResourceOptions> | undefined, errstream: NodeJS.WritableStream | null): Promise<SimpleResourceResult | undefined> {
153153
if (!options) {
154154
return;
155155
}
@@ -199,7 +199,7 @@ export function combineTransformFunctions(transformations: readonly TransformFun
199199
* If there are no options given for this resource or if the platform or
200200
* source images are not suitable, this function resolves with `undefined`.
201201
*/
202-
export async function safelyGenerateAdaptiveIconResources(platform: Platform, resourcesDirectory: string, options?: Readonly<AdaptiveIconResourceOptions>, errstream?: NodeJS.WritableStream): Promise<RunPlatformResult | undefined> {
202+
export async function safelyGenerateAdaptiveIconResources(platform: Platform, resourcesDirectory: string, options: Readonly<AdaptiveIconResourceOptions> | undefined, errstream: NodeJS.WritableStream | null): Promise<RunPlatformResult | undefined> {
203203
if (!options || platform !== Platform.ANDROID) {
204204
return;
205205
}
@@ -218,7 +218,7 @@ export async function safelyGenerateAdaptiveIconResources(platform: Platform, re
218218
/**
219219
* Generate Android Adaptive Icons.
220220
*/
221-
export async function generateAdaptiveIconResources(resourcesDirectory: string, options: Readonly<AdaptiveIconResourceOptions>, errstream?: NodeJS.WritableStream): Promise<RunPlatformResult> {
221+
export async function generateAdaptiveIconResources(resourcesDirectory: string, options: Readonly<AdaptiveIconResourceOptions>, errstream: NodeJS.WritableStream | null): Promise<RunPlatformResult> {
222222
if (options.foreground.sources.length === 0 || options.background.sources.length === 0) {
223223
throw new BadInputError('Adaptive icons require sources for both foreground and background.');
224224
}
@@ -271,7 +271,7 @@ export async function consolidateAdaptiveIconResources(foregrounds: readonly Gen
271271
/**
272272
* Generate the foreground of Adaptive Icons.
273273
*/
274-
export async function generateAdaptiveIconResourcesPortion(resourcesDirectory: string, type: ResourceKey.FOREGROUND | ResourceKey.BACKGROUND, sources: readonly (string | ImageSource)[], transform: TransformFunction = (image, pipeline) => pipeline, errstream?: NodeJS.WritableStream): Promise<SimpleResourceResult> {
274+
export async function generateAdaptiveIconResourcesPortion(resourcesDirectory: string, type: ResourceKey.FOREGROUND | ResourceKey.BACKGROUND, sources: readonly (string | ImageSource)[], transform: TransformFunction = (image, pipeline) => pipeline, errstream: NodeJS.WritableStream | null): Promise<SimpleResourceResult> {
275275
const source = await resolveSourceImage(Platform.ANDROID, ResourceType.ADAPTIVE_ICON, sources.map(s => imageSourceToPath(s)), errstream);
276276

277277
return {
@@ -280,7 +280,7 @@ export async function generateAdaptiveIconResourcesPortion(resourcesDirectory: s
280280
};
281281
}
282282

283-
export async function generateAdaptiveIconResourcesPortionFromImageSource(resourcesDirectory: string, type: ResourceKey.FOREGROUND | ResourceKey.BACKGROUND, source: ResolvedImageSource, transform: TransformFunction = (image, pipeline) => pipeline, errstream?: NodeJS.WritableStream): Promise<GeneratedResource[]> {
283+
export async function generateAdaptiveIconResourcesPortionFromImageSource(resourcesDirectory: string, type: ResourceKey.FOREGROUND | ResourceKey.BACKGROUND, source: ResolvedImageSource, transform: TransformFunction = (image, pipeline) => pipeline, errstream: NodeJS.WritableStream | null): Promise<GeneratedResource[]> {
284284
debug('Using %O for %s source image for %s', source.image.src, ResourceType.ADAPTIVE_ICON, Platform.ANDROID);
285285

286286
const config = getResourcesConfig(Platform.ANDROID, ResourceType.ADAPTIVE_ICON);
@@ -302,7 +302,7 @@ export async function generateAdaptiveIconResourcesPortionFromImageSource(resour
302302
return resources;
303303
}
304304

305-
export async function generateImageResource(type: ResourceType, platform: Platform, resourcesDirectory: string, config: ResourcesTypeConfig<ResourceKeyValues, ResourceKey>, image: ImageSourceData, schema: ResourceKeyValues & ImageSchema, transform: TransformFunction = (image, pipeline) => pipeline, errstream?: NodeJS.WritableStream): Promise<GeneratedResource> {
305+
export async function generateImageResource(type: ResourceType, platform: Platform, resourcesDirectory: string, config: ResourcesTypeConfig<ResourceKeyValues, ResourceKey>, image: ImageSourceData, schema: ResourceKeyValues & ImageSchema, transform: TransformFunction = (image, pipeline) => pipeline, errstream: NodeJS.WritableStream | null): Promise<GeneratedResource> {
306306
const { pipeline, metadata } = image;
307307
const { src, format, width, height } = schema;
308308
const { nodeName, nodeAttributes, indexAttribute, includedResources } = config.configXml;
@@ -336,13 +336,13 @@ export function imageSourceToPath(source: string | ImageSource): string {
336336
return typeof source === 'string' ? source : source.src;
337337
}
338338

339-
export async function resolveSource(platform: Platform, type: ResourceType, name: string, sources: readonly (string | ImageSource | ColorSource)[], errstream?: NodeJS.WritableStream): Promise<ResolvedSource> {
339+
export async function resolveSource(platform: Platform, type: ResourceType, name: string, sources: readonly (string | ImageSource | ColorSource)[], errstream: NodeJS.WritableStream | null): Promise<ResolvedSource> {
340340
for (const source of sources) {
341341
if (typeof source === 'string' || source.type === SourceType.RASTER) {
342342
const src = imageSourceToPath(source);
343343

344344
try {
345-
return await readSourceImage(platform, type, src);
345+
return await readSourceImage(platform, type, src, errstream);
346346
} catch (e) {
347347
debugSourceImage(src, e, errstream);
348348
}

0 commit comments

Comments
 (0)