@travetto/image 4.0.0-rc.0 → 4.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ yarn add @travetto/image
15
15
 
16
16
  This module provides functionality for image resizing, and png optimization. This is primarily meant to be used in conjunction with other modules, like the [Asset](https://github.com/travetto/travetto/tree/main/module/asset#readme "Modular library for storing and retrieving binary assets") module or the [Email Compilation Support](https://github.com/travetto/travetto/tree/main/module/email-compiler#readme "Email compiling module") module. It can also be invoked directly as needed (as it can be very handy for batch processing images on the command line).
17
17
 
18
- The utility's primary structure revolves around the [CommandOperation](https://github.com/travetto/travetto/tree/main/module/command/src/command.ts#L11) from the [Command](https://github.com/travetto/travetto/tree/main/module/command#readme "Support for executing complex commands at runtime.") module. The [CommandOperation](https://github.com/travetto/travetto/tree/main/module/command/src/command.ts#L11) allows for declaration of a local executable, and a fall-back docker container (mainly meant for development). The [ImageConverter](https://github.com/travetto/travetto/tree/main/module/image/src/convert.ts#L35) utilizes [ImageMagick](https://imagemagick.org/index.php), [pngquant](https://pngquant.org/), and [Jpegoptim](https://github.com/tjko/jpegoptim) as the backing for image resizing and png compression, respectively.
18
+ The utility's primary structure revolves around the [CommandOperation](https://github.com/travetto/travetto/tree/main/module/command/src/command.ts#L12) from the [Command](https://github.com/travetto/travetto/tree/main/module/command#readme "Support for executing complex commands at runtime.") module. The [CommandOperation](https://github.com/travetto/travetto/tree/main/module/command/src/command.ts#L12) allows for declaration of a local executable, and a fall-back docker container (mainly meant for development). The [ImageConverter](https://github.com/travetto/travetto/tree/main/module/image/src/convert.ts#L37) utilizes [ImageMagick](https://imagemagick.org/index.php), [pngquant](https://pngquant.org/), and [Jpegoptim](https://github.com/tjko/jpegoptim) as the backing for image resizing and png compression, respectively.
19
19
 
20
20
  **Code: Simple Image Resize**
21
21
  ```typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/image",
3
- "version": "4.0.0-rc.0",
3
+ "version": "4.0.0-rc.2",
4
4
  "description": "Image support, resizing, and optimization",
5
5
  "keywords": [
6
6
  "images",
@@ -23,8 +23,8 @@
23
23
  "directory": "module/image"
24
24
  },
25
25
  "dependencies": {
26
- "@travetto/base": "^4.0.0-rc.0",
27
- "@travetto/command": "^4.0.0-rc.0"
26
+ "@travetto/base": "^4.0.0-rc.2",
27
+ "@travetto/command": "^4.0.0-rc.2"
28
28
  },
29
29
  "travetto": {
30
30
  "displayName": "Image"
package/src/convert.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { Readable } from 'node:stream';
2
2
  import { createReadStream } from 'node:fs';
3
+ import { ChildProcess } from 'node:child_process';
4
+ import { pipeline } from 'node:stream/promises';
3
5
 
4
6
  import { CommandOperation } from '@travetto/command';
5
- import { StreamUtil } from '@travetto/base';
7
+ import { MemoryWritable, StreamUtil } from '@travetto/base';
6
8
 
7
9
  /**
8
10
  * Image output options
@@ -58,37 +60,53 @@ export class ImageConverter {
58
60
  localCheck: ['jpegoptim', ['-h']]
59
61
  });
60
62
 
63
+ static async #stream<T extends ImageType>(proc: ChildProcess, input: T): Promise<T> {
64
+ if (Buffer.isBuffer(input)) {
65
+ const buffer = new MemoryWritable();
66
+ await Promise.all([
67
+ pipeline(await StreamUtil.toStream(input), proc.stdin!),
68
+ pipeline(proc.stdout!, buffer)
69
+ ]);
70
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
71
+ return buffer.toBuffer() as T;
72
+ } else {
73
+ input.pipe(proc.stdin!); // Start the process
74
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
75
+ return proc.stdout! as T;
76
+ }
77
+ }
78
+
61
79
  /**
62
80
  * Resize image using imagemagick
63
81
  */
64
82
  static async resize<T extends ImageType>(image: T, options: ImageOptions): Promise<T> {
65
83
  const dims = [options.w, options.h].map(d => (d && options.strictResolution !== false) ? `${d}!` : d).join('x');
66
84
 
67
- const state = await this.CONVERTER.exec(
85
+ const proc = await this.CONVERTER.exec(
68
86
  'gm', 'convert', '-resize', dims, '-auto-orient',
69
87
  ...(options.optimize ? ['-strip', '-quality', '86'] : []),
70
88
  '-', '-');
71
89
 
72
- return await StreamUtil.execPipe(state, image);
90
+ return this.#stream(proc, image);
73
91
  }
74
92
 
75
93
  /**
76
94
  * Optimize png using pngquant
77
95
  */
78
96
  static async optimize<T extends ImageType>(format: 'png' | 'jpeg', image: T): Promise<T> {
79
- let stream;
97
+ let proc: ChildProcess;
80
98
  switch (format) {
81
99
  case 'png': {
82
- stream = await this.PNG_COMPRESSOR.exec(
100
+ proc = await this.PNG_COMPRESSOR.exec(
83
101
  'pngquant', '--quality', '40-80', '--speed', '1', '--force', '-');
84
102
  break;
85
103
  }
86
104
  case 'jpeg': {
87
- stream = await this.JPEG_COMPRESSOR.exec('jpegoptim', '-m70', '-s', '--stdin', '--stdout');
105
+ proc = await this.JPEG_COMPRESSOR.exec('jpegoptim', '-m70', '-s', '--stdin', '--stdout');
88
106
  break;
89
107
  }
90
108
  }
91
- return await StreamUtil.execPipe(stream, image);
109
+ return this.#stream(proc, image);
92
110
  }
93
111
 
94
112
  /**
@@ -96,7 +114,7 @@ export class ImageConverter {
96
114
  * @param image
97
115
  */
98
116
  static async getDimensions(image: Readable | Buffer | string): Promise<{ width: number, height: number }> {
99
- const state = await this.CONVERTER.exec(
117
+ const proc = await this.CONVERTER.exec(
100
118
  'gm', 'identify', '-format', '%wX%h', '-',
101
119
  );
102
120
 
@@ -104,10 +122,13 @@ export class ImageConverter {
104
122
  image = createReadStream(image);
105
123
  }
106
124
 
107
- await StreamUtil.execPipe(state, await StreamUtil.toStream(image));
125
+ const buffer = new MemoryWritable();
126
+ await Promise.all([
127
+ pipeline(image, proc.stdin!),
128
+ pipeline(proc.stdout!, buffer)
129
+ ]);
108
130
 
109
- const buf = await StreamUtil.toBuffer(state.process.stdout!);
110
- const text = buf.toString('utf8');
131
+ const text = buffer.toBuffer('utf8');
111
132
  const [w, h] = text.split('X').map(x => parseFloat(x));
112
133
 
113
134
  return { width: w, height: h };
package/src/resource.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import { Readable } from 'node:stream';
3
+ import { pipeline } from 'node:stream/promises';
3
4
 
4
- import { ManifestFileUtil, RuntimeIndex, path } from '@travetto/manifest';
5
- import { Env, ResourceLoader, StreamUtil } from '@travetto/base';
5
+ import { RuntimeContext, path } from '@travetto/manifest';
6
+ import { Env, ResourceLoader } from '@travetto/base';
6
7
 
7
8
  import { ImageConverter } from './convert';
8
9
 
@@ -16,7 +17,7 @@ export class ImageOptimizingResourceLoader extends ResourceLoader {
16
17
  constructor(paths: string[] = [], cacheRoot?: string) {
17
18
  super(paths);
18
19
 
19
- this.#cacheRoot = cacheRoot ?? path.resolve(Env.TRV_IMAGE_CACHE.val || ManifestFileUtil.toolPath(RuntimeIndex, 'image_cache'));
20
+ this.#cacheRoot = cacheRoot ?? path.resolve(Env.TRV_IMAGE_CACHE.val || RuntimeContext.toolPath('image_cache'));
20
21
  }
21
22
 
22
23
  async #openFile(pth: string): Promise<fs.FileHandle> {
@@ -40,7 +41,7 @@ export class ImageOptimizingResourceLoader extends ResourceLoader {
40
41
  } else if (/[.]jpe?g$/i.test(rel)) {
41
42
  stream = await ImageConverter.optimize('jpeg', stream);
42
43
  }
43
- await StreamUtil.pipe(stream, handle.createWriteStream());
44
+ await pipeline(stream, handle.createWriteStream());
44
45
  }
45
46
 
46
47
  const buffer = await handle.readFile();