wasm-image-optimization 1.2.2 → 1.2.3

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.
Files changed (2) hide show
  1. package/README.md +25 -289
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -17,6 +17,31 @@
17
17
  - Web Worker (Browser) Multi process
18
18
  `import { optimizeImage } from 'wasm-image-optimization/web-worker';`
19
19
 
20
+ ## Samples
21
+
22
+ https://github.com/SoraKumo001/wasm-image-optimization-samples
23
+
24
+ ### cloudflare-ogp
25
+
26
+ Sample for generating OGP images on Cloudflare Workers.
27
+
28
+ ### deno-ogp
29
+
30
+ Sample for generating OGP images on Deno.
31
+
32
+ ### cloudflare-image-optimization
33
+
34
+ Sample for image optimization on Cloudflare Workers.
35
+
36
+ ### deno-image-optimization
37
+
38
+ Sample for image optimization on Deno.
39
+
40
+ ### node-image-convert
41
+
42
+ Sample for converting image formats on Node.js.
43
+ Single-threaded and multi-threaded operation can be selected.
44
+
20
45
  ## WebWorker on Vite
21
46
 
22
47
  - vite.config.ts
@@ -68,292 +93,3 @@ optimizeImageExt({
68
93
  - png
69
94
  - webp
70
95
  - avif(default)
71
-
72
- ## usage
73
-
74
- ### Node.js (CLI)
75
-
76
- ```ts
77
- import fs from "node:fs";
78
- import {
79
- optimizeImage,
80
- waitAll,
81
- close,
82
- } from "wasm-image-optimization/node-worker";
83
-
84
- const formats = ["webp", "jpeg", "png", "avif"] as const;
85
-
86
- const main = async () => {
87
- fs.mkdirSync("./image_output", { recursive: true });
88
- const files = fs.readdirSync("./images");
89
- for (const file of files) {
90
- const image = fs.readFileSync(`./images/${file}`);
91
- for (const format of formats) {
92
- optimizeImage({
93
- image,
94
- quality: 100,
95
- format,
96
- width: 1000,
97
- }).then((encoded) => {
98
- console.log(
99
- !!encoded,
100
- file,
101
- format,
102
- encoded && `${Math.floor(encoded.length / 1024)}KB`
103
- );
104
- if (encoded) {
105
- const fileName = file.split(".")[0];
106
- fs.promises.writeFile(`image_output/${fileName}.${format}`, encoded);
107
- }
108
- });
109
- }
110
- }
111
- await waitAll(); // Wait for all workers to finish before exiting the program.
112
- close(); // Close all workers
113
- console.log("exit");
114
- };
115
- main();
116
- ```
117
-
118
- ### Next.js image optimization with Cloudflare
119
-
120
- ```ts
121
- import { optimizeImage } from "wasm-image-optimization";
122
-
123
- const isValidUrl = (url: string) => {
124
- try {
125
- new URL(url);
126
- return true;
127
- } catch (err) {
128
- return false;
129
- }
130
- };
131
-
132
- const handleRequest = async (
133
- request: Request,
134
- _env: {},
135
- ctx: ExecutionContext
136
- ): Promise<Response> => {
137
- const accept = request.headers.get("accept");
138
- const isWebp =
139
- accept
140
- ?.split(",")
141
- .map((format) => format.trim())
142
- .some((format) => ["image/webp", "*/*", "image/*"].includes(format)) ??
143
- true;
144
- const isAvif =
145
- accept
146
- ?.split(",")
147
- .map((format) => format.trim())
148
- .some((format) => ["image/avif", "*/*", "image/*"].includes(format)) ??
149
- true;
150
-
151
- const url = new URL(request.url);
152
-
153
- const params = url.searchParams;
154
- const imageUrl = params.get("url");
155
- if (!imageUrl || !isValidUrl(imageUrl)) {
156
- return new Response("url is required", { status: 400 });
157
- }
158
-
159
- const cache = caches.default;
160
- url.searchParams.append("webp", isWebp.toString());
161
- const cacheKey = new Request(url.toString());
162
- const cachedResponse = await cache.match(cacheKey);
163
- if (cachedResponse) {
164
- return cachedResponse;
165
- }
166
-
167
- const width = params.get("w");
168
- const quality = params.get("q");
169
-
170
- const [srcImage, contentType] = await fetch(imageUrl, {
171
- cf: { cacheKey: imageUrl },
172
- })
173
- .then(async (res) =>
174
- res.ok
175
- ? ([await res.arrayBuffer(), res.headers.get("content-type")] as const)
176
- : []
177
- )
178
- .catch(() => []);
179
-
180
- if (!srcImage) {
181
- return new Response("image not found", { status: 404 });
182
- }
183
-
184
- if (contentType && ["image/svg+xml", "image/gif"].includes(contentType)) {
185
- const response = new Response(srcImage, {
186
- headers: {
187
- "Content-Type": contentType,
188
- "Cache-Control": "public, max-age=31536000, immutable",
189
- },
190
- });
191
- ctx.waitUntil(cache.put(cacheKey, response.clone()));
192
- return response;
193
- }
194
-
195
- const format = isAvif
196
- ? "avif"
197
- : isWebp
198
- ? "webp"
199
- : contentType === "image/jpeg"
200
- ? "jpeg"
201
- : "png";
202
- const image = await optimizeImage({
203
- image: srcImage,
204
- width: width ? parseInt(width) : undefined,
205
- quality: quality ? parseInt(quality) : undefined,
206
- format,
207
- });
208
- const response = new Response(image, {
209
- headers: {
210
- "Content-Type": `image/${format}`,
211
- "Cache-Control": "public, max-age=31536000, immutable",
212
- date: new Date().toUTCString(),
213
- },
214
- });
215
- ctx.waitUntil(cache.put(cacheKey, response.clone()));
216
- return response;
217
- };
218
-
219
- export default {
220
- fetch: handleRequest,
221
- };
222
- ```
223
-
224
- - next.config.js
225
-
226
- ```js
227
- /**
228
- * @type { import("next").NextConfig}
229
- */
230
- const config = {
231
- images: {
232
- path: "https://xxx.yyy.workers.dev/",
233
- },
234
- };
235
- export default config;
236
- ```
237
-
238
- ### Deno Deploy
239
-
240
- #### Parameters
241
-
242
- | Name | Type | Description |
243
- | ---- | ------ | ----------- |
244
- | url | string | Image URL |
245
- | q | number | Quality |
246
- | w | number | Width |
247
-
248
- https://xxxx.deno.dev/?url=https://xxx.png&q=80&w=200
249
-
250
- ```ts
251
- import { optimizeImage } from "npm:wasm-image-optimization/esm";
252
-
253
- const isValidUrl = (url: string) => {
254
- try {
255
- new URL(url);
256
- return true;
257
- } catch (_e) {
258
- return false;
259
- }
260
- };
261
-
262
- const isType = (accept: string | null, type: string) => {
263
- return (
264
- accept
265
- ?.split(",")
266
- .map((format) => format.trim())
267
- .some((format) => [`image/${type}`, "*/*", "image/*"].includes(format)) ??
268
- true
269
- );
270
- };
271
-
272
- Deno.serve(async (request) => {
273
- const url = new URL(request.url);
274
- const params = url.searchParams;
275
- const type = ["avif", "webp", "png", "jpeg"].find(
276
- (v) => v === params.get("type")
277
- ) as "avif" | "webp" | "png" | "jpeg" | undefined;
278
- const accept = request.headers.get("accept");
279
- const isAvif = isType(accept, "avif");
280
- const isWebp = isType(accept, "webp");
281
-
282
- const cache = await caches.open(
283
- `image-${isAvif ? "-avif" : ""}${isWebp ? "-webp" : ""}`
284
- );
285
-
286
- const cached = await cache.match(request);
287
- if (cached) {
288
- return cached;
289
- }
290
-
291
- const imageUrl = params.get("url");
292
- if (!imageUrl || !isValidUrl(imageUrl)) {
293
- return new Response("url is required", { status: 400 });
294
- }
295
-
296
- if (isAvif) {
297
- url.searchParams.append("avif", isAvif.toString());
298
- } else if (isWebp) {
299
- url.searchParams.append("webp", isWebp.toString());
300
- }
301
-
302
- const cacheKey = new Request(url.toString());
303
- const cachedResponse = await cache.match(cacheKey);
304
- if (cachedResponse) {
305
- return cachedResponse;
306
- }
307
-
308
- const width = params.get("w");
309
- const quality = params.get("q");
310
-
311
- const [srcImage, contentType] = await fetch(imageUrl)
312
- .then(async (res) =>
313
- res.ok
314
- ? ([await res.arrayBuffer(), res.headers.get("content-type")] as const)
315
- : []
316
- )
317
- .catch(() => []);
318
-
319
- if (!srcImage) {
320
- return new Response("image not found", { status: 404 });
321
- }
322
-
323
- if (contentType && ["image/svg+xml", "image/gif"].includes(contentType)) {
324
- const response = new Response(srcImage, {
325
- headers: {
326
- "Content-Type": contentType,
327
- "Cache-Control": "public, max-age=31536000, immutable",
328
- },
329
- });
330
- cache.put(request, response.clone());
331
- return response;
332
- }
333
-
334
- const format =
335
- type ??
336
- (isAvif
337
- ? "avif"
338
- : isWebp
339
- ? "webp"
340
- : contentType === "image/jpeg"
341
- ? "jpeg"
342
- : "png");
343
- const image = await optimizeImage({
344
- image: srcImage,
345
- width: width ? parseInt(width) : undefined,
346
- quality: quality ? parseInt(quality) : undefined,
347
- format,
348
- });
349
- const response = new Response(image, {
350
- headers: {
351
- "Content-Type": `image/${format}`,
352
- "Cache-Control": "public, max-age=31536000, immutable",
353
- date: new Date().toUTCString(),
354
- },
355
- });
356
- cache.put(request, response.clone());
357
- return response;
358
- });
359
- ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wasm-image-optimization",
3
3
  "description": "Optimize images with wasm on edge runtime",
4
- "version": "1.2.2",
4
+ "version": "1.2.3",
5
5
  "scripts": {
6
6
  "test": "yarn ts-node test",
7
7
  "lint:fix": "eslint --fix src/ && prettier -w src",
@@ -92,7 +92,7 @@
92
92
  },
93
93
  "license": "MIT",
94
94
  "dependencies": {
95
- "worker-lib": "2.0.0"
95
+ "worker-lib": "2.0.2"
96
96
  },
97
97
  "devDependencies": {
98
98
  "@eslint/js": "9.21.0",