@divriots/jampack 0.23.4 → 0.24.0

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/LICENSE CHANGED
File without changes
package/README.md CHANGED
File without changes
package/dist/cache.js CHANGED
@@ -1,4 +1,4 @@
1
- import hasha from 'hasha';
1
+ import { hashSync as hasha } from 'hasha';
2
2
  import path from 'path';
3
3
  import * as fs from 'fs/promises';
4
4
  import $state from './state.js';
package/dist/compress.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as fs from 'fs/promises';
2
2
  import * as path from 'path';
3
3
  import { formatBytes } from './utils.js';
4
+ import config from './config.js';
4
5
  import $state from './state.js';
5
6
  import { globby } from 'globby';
6
7
  import ora from 'ora';
@@ -19,10 +20,12 @@ const processFile = async (file, stats) => {
19
20
  case '.svg':
20
21
  case '.webp':
21
22
  case '.avif':
22
- const imgData = await fs.readFile(file);
23
- const newImage = await compressImage(imgData, {});
24
- if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < stats.size) {
25
- writeData = newImage.data;
23
+ if (config.image.compress) {
24
+ const imgData = await fs.readFile(file);
25
+ const newImage = await compressImage(imgData, {});
26
+ if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < stats.size) {
27
+ writeData = newImage.data;
28
+ }
26
29
  }
27
30
  break;
28
31
  case '.html':
@@ -10,6 +10,6 @@ export type Image = {
10
10
  };
11
11
  export type ImageOutputOptions = {
12
12
  resize?: sharp.ResizeOptions;
13
- toFormat?: 'webp' | 'avif' | 'png' | 'jpeg' | 'jpeg+progressive' | 'unchanged';
13
+ toFormat?: 'webp' | 'avif' | 'png' | 'jpeg' | 'unchanged';
14
14
  };
15
15
  export declare function compressImage(data: Buffer, options: ImageOutputOptions): Promise<Image | undefined>;
@@ -19,7 +19,8 @@ export async function compressImage(data, options) {
19
19
  }
20
20
  // Load modifiable toFormat
21
21
  let toFormat = options.toFormat || 'unchanged';
22
- let sharpFile = await sharp(data, { animated: true });
22
+ let sharpFile = sharp(data, { animated: true });
23
+ sharpFile = sharpFile.rotate(); // Rotate image based on EXIF data (because EXIF data is removed)
23
24
  const meta = await sharpFile.metadata();
24
25
  if (meta.pages && meta.pages > 1) {
25
26
  // Skip animated images for the moment.
@@ -85,10 +86,6 @@ export async function compressImage(data, options) {
85
86
  sharpFile = sharpFile.jpeg({ ...config.image.jpeg.options } || {});
86
87
  outputFormat = 'jpg';
87
88
  break;
88
- case 'jpeg+progressive':
89
- sharpFile = sharpFile.jpeg({ ...config.image.jpeg.options, progressive: true } || {});
90
- outputFormat = 'jpg';
91
- break;
92
89
  case 'png':
93
90
  sharpFile = sharpFile.png({ ...config.image.png.options } || {});
94
91
  outputFormat = 'png';
package/dist/index.js CHANGED
File without changes
package/dist/optimize.js CHANGED
@@ -201,7 +201,7 @@ function getTheFold($) {
201
201
  return 0;
202
202
  }
203
203
  async function processImage(htmlfile, img, isAboveTheFold) {
204
- var _a, _b, _c;
204
+ var _a, _b;
205
205
  /*
206
206
  * Attribute 'src'
207
207
  */
@@ -350,21 +350,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
350
350
  let newImage;
351
351
  const isInPicture = (_a = img.parent()) === null || _a === void 0 ? void 0 : _a.is('picture');
352
352
  let srcToFormat = 'unchanged';
353
- if (isAboveTheFold) {
354
- // Above the fold, nothing beats progressive JPEG for LCP
355
- // But jpeg doesn't support transparency
356
- if ((_b = (await originalImage.getImageMeta())) === null || _b === void 0 ? void 0 : _b.isOpaque) {
357
- srcToFormat = 'jpeg+progressive';
358
- }
359
- else {
360
- srcToFormat =
361
- (await originalImage.getMime()) === 'image/avif' ||
362
- (await originalImage.getMime()) === 'image/webp'
363
- ? 'unchanged'
364
- : 'webp';
365
- }
366
- }
367
- else if (isInPicture) {
353
+ if (isInPicture) {
368
354
  // srcToFormat should not change if in picture
369
355
  }
370
356
  else {
@@ -381,12 +367,10 @@ async function processImage(htmlfile, img, isAboveTheFold) {
381
367
  resize: { width: config.image.srcset_max_width },
382
368
  });
383
369
  }
384
- if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) &&
385
- (newImage.data.length < (await originalImage.getLen()) ||
386
- srcToFormat === 'jpeg+progressive') // Progressive override all even if worse
387
- ) {
388
- const additionalExtension = `.${newImage.format}`;
389
- const newFilename = originalImage.filePathAbsolute + additionalExtension;
370
+ if ((newImage === null || newImage === void 0 ? void 0 : newImage.data) && newImage.data.length < (await originalImage.getLen())) {
371
+ const newFilename = path.join(path.dirname(originalImage.filePathAbsolute), path.basename(originalImage.filePathAbsolute, path.extname(originalImage.filePathAbsolute)) + `.${newImage.format}`);
372
+ const newSrc = path.join(path.dirname(attrib_src), path.basename(attrib_src, path.extname(attrib_src)) +
373
+ `.${newImage.format}`);
390
374
  if (!$state.compressedFiles.has(newFilename) && !$state.args.nowrite) {
391
375
  fs.writeFile(newFilename, newImage.data);
392
376
  }
@@ -400,7 +384,7 @@ async function processImage(htmlfile, img, isAboveTheFold) {
400
384
  originalSize: await originalImage.getLen(),
401
385
  compressedSize: newImage.data.length,
402
386
  });
403
- img.attr('src', attrib_src + additionalExtension);
387
+ img.attr('src', newSrc);
404
388
  }
405
389
  else {
406
390
  // Drop new Image
@@ -502,71 +486,56 @@ async function processImage(htmlfile, img, isAboveTheFold) {
502
486
  */
503
487
  if (isInPicture) {
504
488
  const picture = img.parent();
505
- if (isAboveTheFold && srcToFormat === 'jpeg+progressive') {
506
- // We don't need anything else than jpeg progressive above the fold
507
- const nodes = picture.children(`source`);
508
- nodes.remove();
509
- }
510
- else {
511
- // List of possible sources to generate
512
- //
513
- const sourcesToGenerate = [];
514
- // Only try to generate better images
515
- // TODO generate more compatible formats (avif => webp/jpeg/png)
516
- //
517
- const mime = await originalImage.getMime();
518
- switch (mime) {
519
- case 'image/png':
520
- case 'image/jpeg':
489
+ // List of possible sources to generate
490
+ //
491
+ const sourcesToGenerate = [];
492
+ // Only try to generate better images
493
+ // TODO generate more compatible formats (avif => webp/jpeg/png)
494
+ //
495
+ const mime = await originalImage.getMime();
496
+ switch (mime) {
497
+ case 'image/png':
498
+ case 'image/jpeg':
499
+ sourcesToGenerate.push({
500
+ mime: 'image/webp',
501
+ format: 'webp',
502
+ });
503
+ case 'image/webp':
504
+ // Only add AVIF source for lossy images
505
+ // AVIF don't perform well on lossless images
506
+ // PNG and WebP will perform better
507
+ // https://www.reddit.com/r/jpegxl/comments/l9ta2u/how_does_lossless_jpegxl_compared_to_png/
508
+ // https://twitter.com/jonsneyers/status/1346389917816008704?s=19
509
+ const isLossless = mime === 'image/png' ||
510
+ (mime === 'image/webp' &&
511
+ ((_b = (await originalImage.getImageMeta())) === null || _b === void 0 ? void 0 : _b.isLossless));
512
+ if (!isLossless) {
521
513
  sourcesToGenerate.push({
522
- mime: 'image/webp',
523
- format: 'webp',
514
+ mime: 'image/avif',
515
+ format: 'avif',
524
516
  });
525
- case 'image/webp':
526
- // Only add AVIF source for lossy images
527
- // AVIF don't perform well on lossless images
528
- // PNG and WebP will perform better
529
- // https://www.reddit.com/r/jpegxl/comments/l9ta2u/how_does_lossless_jpegxl_compared_to_png/
530
- // https://twitter.com/jonsneyers/status/1346389917816008704?s=19
531
- const isLossless = mime === 'image/png' ||
532
- (mime === 'image/webp' &&
533
- ((_c = (await originalImage.getImageMeta())) === null || _c === void 0 ? void 0 : _c.isLossless));
534
- if (!isLossless) {
535
- sourcesToGenerate.push({
536
- mime: 'image/avif',
537
- format: 'avif',
538
- });
539
- }
540
- case 'image/avif':
541
- // Nothing better
542
- break;
543
- }
544
- const sizes = img.attr('sizes');
545
- for (const s of sourcesToGenerate) {
546
- const sourceWithThisMimeType = picture.children(`source[type="${s.mime}"]`);
547
- if (sourceWithThisMimeType.length > 0) {
548
- // Ignore the creation of sources that already exist
549
- continue;
550
- }
551
- const srcset = await generateSrcSet(htmlfile, originalImage, undefined, undefined, {
552
- toFormat: s.format,
553
- });
554
- if (!srcset) {
555
- // No sourceset generated
556
- // Image is too small or can't be compressed better
557
- continue;
558
517
  }
559
- const source = `<source ${sizes ? `sizes="${sizes}"` : ''} srcset="${srcset}" type="${s.mime}">`;
560
- picture.prepend(source);
518
+ case 'image/avif':
519
+ // Nothing better
520
+ break;
521
+ }
522
+ const sizes = img.attr('sizes');
523
+ for (const s of sourcesToGenerate.reverse()) {
524
+ const sourceWithThisMimeType = picture.children(`source[type="${s.mime}"]`);
525
+ if (sourceWithThisMimeType.length > 0) {
526
+ // Ignore the creation of sources that already exist
527
+ continue;
561
528
  }
562
- // Reorder sources to priority order 1)avif 2)webp
563
- //
564
- function popupSource(type) {
565
- const nodes = picture.children(`source[type="${type}"]`);
566
- picture.prepend(nodes);
529
+ const srcset = await generateSrcSet(htmlfile, originalImage, undefined, undefined, {
530
+ toFormat: s.format,
531
+ });
532
+ if (!srcset) {
533
+ // No sourceset generated
534
+ // Image is too small or can't be compressed better
535
+ continue;
567
536
  }
568
- popupSource('image/webp');
569
- popupSource('image/avif');
537
+ const source = `<source ${sizes ? `sizes="${sizes}"` : ''} srcset="${srcset}" type="${s.mime}">`;
538
+ img.before(source); // Append before this way existing sources are always top priority
570
539
  }
571
540
  }
572
541
  }
@@ -1,7 +1,7 @@
1
1
  import * as path from 'path';
2
2
  import * as fs from 'fs/promises';
3
3
  import $state from '../state.js';
4
- import hasha from 'hasha';
4
+ import { hashSync as hasha } from 'hasha';
5
5
  import { fileTypeFromBuffer } from 'file-type';
6
6
  import { addToCache, getFromCache } from '../cache.js';
7
7
  import { parse } from '../utils/cache-control-parser.js';
@@ -43,7 +43,9 @@ export class Resource {
43
43
  // TODO
44
44
  // Check for lossless avif
45
45
  case 'jpg':
46
- let sharpFile = await sharp(await this.getData(), { animated: true });
46
+ let sharpFile = sharp(await this.getData(), {
47
+ animated: true,
48
+ });
47
49
  const meta = await sharpFile.metadata();
48
50
  const stats = await sharpFile.stats();
49
51
  this.image_meta = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@divriots/jampack",
3
- "version": "0.23.4",
3
+ "version": "0.24.0",
4
4
  "cache-version": {
5
5
  "img": "v1",
6
6
  "img-ext": "v1"
@@ -28,48 +28,47 @@
28
28
  "dependencies": {
29
29
  "@divriots/cheerio": "1.0.0-rc.12",
30
30
  "@proload/core": "^0.3.3",
31
- "@swc/core": "^1.3.100",
31
+ "@swc/core": "^1.4.16",
32
32
  "add": "^2.0.6",
33
- "browserslist": "^4.22.2",
34
- "commander": "^10.0.1",
35
- "critters": "^0.0.20",
33
+ "browserslist": "^4.23.0",
34
+ "commander": "^12.0.0",
35
+ "critters": "^0.0.22",
36
36
  "deepmerge": "^4.3.1",
37
- "esbuild": "^0.19.9",
38
- "file-type": "^18.7.0",
39
- "globby": "^13.2.2",
40
- "hasha": "^5.2.2",
37
+ "esbuild": "^0.20.2",
38
+ "file-type": "^19.0.0",
39
+ "globby": "^14.0.1",
40
+ "hasha": "^6.0.0",
41
41
  "html-minifier-terser": "^7.2.0",
42
42
  "kleur": "^4.1.5",
43
- "lightningcss": "^1.22.1",
43
+ "lightningcss": "^1.24.1",
44
44
  "mini-svg-data-uri": "^1.4.4",
45
- "ora": "^7.0.1",
45
+ "ora": "^8.0.1",
46
46
  "quicklink": "^2.3.0",
47
- "sharp": "^0.33.0",
48
- "svgo": "^3.1.0",
49
- "table": "^6.8.1",
50
- "undici": "^5.24.0",
51
- "unpic": "^3.16.0"
47
+ "sharp": "^0.33.3",
48
+ "svgo": "^3.2.0",
49
+ "table": "^6.8.2",
50
+ "undici": "^6.13.0",
51
+ "unpic": "^3.18.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/html-minifier-terser": "^7.0.2",
55
- "@types/node": "^20.10.4",
55
+ "@types/node": "^20.12.7",
56
56
  "shx": "^0.3.3",
57
57
  "ts-node": "^10.9.2",
58
58
  "tslib": "^2.6.2",
59
- "typescript": "^5.3.3"
59
+ "typescript": "^5.4.5"
60
60
  },
61
+ "engines": {
62
+ "node": ">=14.0.0"
63
+ },
64
+ "bugs": "https://github.com/divriots/jampack/issues",
65
+ "keywords": [],
66
+ "types": "dist/index.d.ts",
61
67
  "scripts": {
62
68
  "watch": "pnpm build --watch",
63
69
  "try": "shx rm -fr demo && cp -R demo_drc demo && node ./dist/index.js ./demo --nocache",
64
70
  "build": "shx rm -rf dist && tsc -b",
65
71
  "lint": "eslint . --ext .ts --config .eslintrc",
66
- "prepublishOnly": "pnpm build",
67
72
  "cleanAllDotJampack": "find -s . | grep '.jampack$' | xargs rm -fr"
68
- },
69
- "engines": {
70
- "node": ">=14.0.0"
71
- },
72
- "bugs": "https://github.com/divriots/jampack/issues",
73
- "keywords": [],
74
- "types": "dist/index.d.ts"
75
- }
73
+ }
74
+ }