@travetto/image 3.4.3 → 4.0.0-rc.1
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 +2 -2
- package/__index__.ts +1 -0
- package/package.json +3 -3
- package/src/convert.ts +34 -13
- package/src/resource.ts +7 -6
- package/src/trv.d.ts +11 -0
package/README.md
CHANGED
|
@@ -15,11 +15,11 @@ 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#
|
|
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
|
|
22
|
-
import { createReadStream } from 'fs';
|
|
22
|
+
import { createReadStream } from 'node:fs';
|
|
23
23
|
|
|
24
24
|
import { StreamUtil } from '@travetto/base';
|
|
25
25
|
import { ImageConverter } from '@travetto/image';
|
package/__index__.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/image",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-rc.1",
|
|
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": "^
|
|
27
|
-
"@travetto/command": "^
|
|
26
|
+
"@travetto/base": "^4.0.0-rc.1",
|
|
27
|
+
"@travetto/command": "^4.0.0-rc.1"
|
|
28
28
|
},
|
|
29
29
|
"travetto": {
|
|
30
30
|
"displayName": "Image"
|
package/src/convert.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { Readable } from 'stream';
|
|
2
|
-
import { createReadStream } from 'fs';
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
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
|
|
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
|
|
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
|
|
97
|
+
let proc: ChildProcess;
|
|
80
98
|
switch (format) {
|
|
81
99
|
case 'png': {
|
|
82
|
-
|
|
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
|
-
|
|
105
|
+
proc = await this.JPEG_COMPRESSOR.exec('jpegoptim', '-m70', '-s', '--stdin', '--stdout');
|
|
88
106
|
break;
|
|
89
107
|
}
|
|
90
108
|
}
|
|
91
|
-
return
|
|
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
|
|
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
|
-
|
|
125
|
+
const buffer = new MemoryWritable();
|
|
126
|
+
await Promise.all([
|
|
127
|
+
pipeline(image, proc.stdin!),
|
|
128
|
+
pipeline(proc.stdout!, buffer)
|
|
129
|
+
]);
|
|
108
130
|
|
|
109
|
-
const
|
|
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
|
-
import fs from 'fs/promises';
|
|
2
|
-
import { Readable } from 'stream';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
-
import { Env, ResourceLoader
|
|
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.
|
|
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
|
|
44
|
+
await pipeline(stream, handle.createWriteStream());
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
const buffer = await handle.readFile();
|